blob: d02ed90e0937b34fc19eb765a1dde264428d1cc5 [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 {
71 LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
72 usb_device_get_product_name(device));
73 } else if (interface->bInterfaceClass == 0xFF &&
74 interface->bInterfaceSubClass == 0xFF &&
75 interface->bInterfaceProtocol == 0) {
76 char* interfaceName = usb_device_get_string(device, interface->iInterface);
77 if (!interfaceName || strcmp(interfaceName, "MTP"))
78 continue;
79 // Looks like an android style MTP device
80 LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
81 usb_device_get_product_name(device));
82 } else {
83 // look for special cased devices based on vendor/product ID
84 // we are doing this mainly for testing purposes
85 uint16_t vendor = usb_device_get_vendor_id(device);
86 uint16_t product = usb_device_get_product_id(device);
87 if (!isMtpDevice(vendor, product)) {
88 // not an MTP or PTP device
89 continue;
90 }
91 // request MTP OS string and descriptor
92 // some music players need to see this before entering MTP mode.
93 char buffer[256];
94 memset(buffer, 0, sizeof(buffer));
95 int ret = usb_device_send_control(device,
96 USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
97 USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
98 0, sizeof(buffer), buffer);
99 printf("usb_device_send_control returned %d errno: %d\n", ret, errno);
100 if (ret > 0) {
101 printf("got MTP string %s\n", buffer);
102 ret = usb_device_send_control(device,
103 USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
104 0, 4, sizeof(buffer), buffer);
105 printf("OS descriptor got %d\n", ret);
106 } else {
107 printf("no MTP string\n");
108 }
109 }
110
111 // if we got here, then we have a likely MTP or PTP device
112
113 // interface should be followed by three endpoints
114 struct usb_endpoint_descriptor *ep;
115 struct usb_endpoint_descriptor *ep_in_desc = NULL;
116 struct usb_endpoint_descriptor *ep_out_desc = NULL;
117 struct usb_endpoint_descriptor *ep_intr_desc = NULL;
118 for (int i = 0; i < 3; i++) {
119 ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
120 if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
121 LOGE("endpoints not found\n");
122 return NULL;
123 }
124 if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
125 if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
126 ep_in_desc = ep;
127 else
128 ep_out_desc = ep;
129 } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
130 ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
131 ep_intr_desc = ep;
132 }
133 }
134 if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
135 LOGE("endpoints not found\n");
136 return NULL;
137 }
138
139 if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
140 LOGE("usb_device_claim_interface failed errno: %d\n", errno);
141 return NULL;
142 }
143
144 MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
145 ep_in_desc, ep_out_desc, ep_intr_desc);
146 mtpDevice->initialize();
147 return mtpDevice;
148 }
149 }
150
151 usb_device_close(device);
152 LOGE("device not found");
153 return NULL;
154}
155
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400156MtpDevice::MtpDevice(struct usb_device* device, int interface,
Mike Lockwood42d0b792011-01-04 14:48:57 -0500157 const struct usb_endpoint_descriptor *ep_in,
158 const struct usb_endpoint_descriptor *ep_out,
159 const struct usb_endpoint_descriptor *ep_intr)
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400160 : mDevice(device),
161 mInterface(interface),
Mike Lockwood42d0b792011-01-04 14:48:57 -0500162 mRequestIn1(NULL),
163 mRequestIn2(NULL),
164 mRequestOut(NULL),
165 mRequestIntr(NULL),
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400166 mDeviceInfo(NULL),
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400167 mSessionID(0),
Mike Lockwoodf7454622010-12-09 18:34:18 -0800168 mTransactionID(0),
169 mReceivedResponse(false)
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400170{
Mike Lockwood42d0b792011-01-04 14:48:57 -0500171 mRequestIn1 = usb_request_new(device, ep_in);
172 mRequestIn2 = usb_request_new(device, ep_in);
173 mRequestOut = usb_request_new(device, ep_out);
174 mRequestIntr = usb_request_new(device, ep_intr);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400175}
176
177MtpDevice::~MtpDevice() {
178 close();
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400179 for (int i = 0; i < mDeviceProperties.size(); i++)
180 delete mDeviceProperties[i];
Mike Lockwood42d0b792011-01-04 14:48:57 -0500181 usb_request_free(mRequestIn1);
182 usb_request_free(mRequestIn2);
183 usb_request_free(mRequestOut);
184 usb_request_free(mRequestIntr);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400185}
186
187void MtpDevice::initialize() {
188 openSession();
189 mDeviceInfo = getDeviceInfo();
190 if (mDeviceInfo) {
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400191 if (mDeviceInfo->mDeviceProperties) {
192 int count = mDeviceInfo->mDeviceProperties->size();
193 for (int i = 0; i < count; i++) {
194 MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
195 MtpProperty* property = getDevicePropDesc(propCode);
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800196 if (property)
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400197 mDeviceProperties.push(property);
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400198 }
199 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400200 }
201}
202
203void MtpDevice::close() {
204 if (mDevice) {
205 usb_device_release_interface(mDevice, mInterface);
206 usb_device_close(mDevice);
207 mDevice = NULL;
208 }
209}
210
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800211void MtpDevice::print() {
212 if (mDeviceInfo) {
213 mDeviceInfo->print();
214
215 if (mDeviceInfo->mDeviceProperties) {
216 LOGI("***** DEVICE PROPERTIES *****\n");
217 int count = mDeviceInfo->mDeviceProperties->size();
218 for (int i = 0; i < count; i++) {
219 MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
220 MtpProperty* property = getDevicePropDesc(propCode);
221 if (property) {
222 property->print();
223 }
224 }
225 }
226 }
227
228 if (mDeviceInfo->mPlaybackFormats) {
229 LOGI("***** OBJECT PROPERTIES *****\n");
230 int count = mDeviceInfo->mPlaybackFormats->size();
231 for (int i = 0; i < count; i++) {
232 MtpObjectFormat format = (*mDeviceInfo->mPlaybackFormats)[i];
233 LOGI("*** FORMAT: %s\n", MtpDebug::getFormatCodeName(format));
234 MtpObjectPropertyList* props = getObjectPropsSupported(format);
235 if (props) {
236 for (int j = 0; j < props->size(); j++) {
237 MtpObjectProperty prop = (*props)[j];
Mike Lockwood99e393a2010-12-07 18:53:04 -0800238 MtpProperty* property = getObjectPropDesc(prop, format);
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800239 if (property)
240 property->print();
241 else
242 LOGE("could not fetch property: %s",
243 MtpDebug::getObjectPropCodeName(prop));
244 }
245 }
246 }
247 }
248}
249
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400250const char* MtpDevice::getDeviceName() {
251 if (mDevice)
252 return usb_device_get_name(mDevice);
253 else
254 return "???";
255}
256
257bool MtpDevice::openSession() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400258 Mutex::Autolock autoLock(mMutex);
259
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400260 mSessionID = 0;
261 mTransactionID = 0;
262 MtpSessionID newSession = 1;
263 mRequest.reset();
264 mRequest.setParameter(1, newSession);
265 if (!sendRequest(MTP_OPERATION_OPEN_SESSION))
266 return false;
267 MtpResponseCode ret = readResponse();
268 if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN)
269 newSession = mResponse.getParameter(1);
270 else if (ret != MTP_RESPONSE_OK)
271 return false;
272
273 mSessionID = newSession;
274 mTransactionID = 1;
275 return true;
276}
277
278bool MtpDevice::closeSession() {
279 // FIXME
280 return true;
281}
282
283MtpDeviceInfo* MtpDevice::getDeviceInfo() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400284 Mutex::Autolock autoLock(mMutex);
285
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400286 mRequest.reset();
287 if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
288 return NULL;
289 if (!readData())
290 return NULL;
291 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400292 if (ret == MTP_RESPONSE_OK) {
293 MtpDeviceInfo* info = new MtpDeviceInfo;
294 info->read(mData);
295 return info;
296 }
297 return NULL;
298}
299
300MtpStorageIDList* MtpDevice::getStorageIDs() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400301 Mutex::Autolock autoLock(mMutex);
302
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400303 mRequest.reset();
304 if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
305 return NULL;
306 if (!readData())
307 return NULL;
308 MtpResponseCode ret = readResponse();
309 if (ret == MTP_RESPONSE_OK) {
310 return mData.getAUInt32();
311 }
312 return NULL;
313}
314
315MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400316 Mutex::Autolock autoLock(mMutex);
317
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400318 mRequest.reset();
319 mRequest.setParameter(1, storageID);
320 if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
321 return NULL;
322 if (!readData())
323 return NULL;
324 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400325 if (ret == MTP_RESPONSE_OK) {
326 MtpStorageInfo* info = new MtpStorageInfo(storageID);
327 info->read(mData);
328 return info;
329 }
330 return NULL;
331}
332
333MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
334 MtpObjectFormat format, MtpObjectHandle parent) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400335 Mutex::Autolock autoLock(mMutex);
336
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400337 mRequest.reset();
338 mRequest.setParameter(1, storageID);
339 mRequest.setParameter(2, format);
340 mRequest.setParameter(3, parent);
341 if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES))
342 return NULL;
343 if (!readData())
344 return NULL;
345 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400346 if (ret == MTP_RESPONSE_OK) {
347 return mData.getAUInt32();
348 }
349 return NULL;
350}
351
352MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400353 Mutex::Autolock autoLock(mMutex);
354
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400355 // FIXME - we might want to add some caching here
356
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400357 mRequest.reset();
358 mRequest.setParameter(1, handle);
359 if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO))
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 MtpObjectInfo* info = new MtpObjectInfo(handle);
366 info->read(mData);
367 return info;
368 }
369 return NULL;
370}
371
Mike Lockwood3e072b32010-06-10 16:34:20 -0400372void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400373 Mutex::Autolock autoLock(mMutex);
374
Mike Lockwood3e072b32010-06-10 16:34:20 -0400375 mRequest.reset();
376 mRequest.setParameter(1, handle);
377 if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
378 MtpResponseCode ret = readResponse();
379 if (ret == MTP_RESPONSE_OK) {
380 return mData.getData(outLength);
381 }
382 }
383 outLength = 0;
384 return NULL;
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400385}
Mike Lockwood3e072b32010-06-10 16:34:20 -0400386
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400387MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) {
388 Mutex::Autolock autoLock(mMutex);
389
390 mRequest.reset();
391 MtpObjectHandle parent = info->mParent;
392 if (parent == 0)
393 parent = MTP_PARENT_ROOT;
394
395 mRequest.setParameter(1, info->mStorageID);
396 mRequest.setParameter(2, info->mParent);
397
398 mData.putUInt32(info->mStorageID);
399 mData.putUInt16(info->mFormat);
400 mData.putUInt16(info->mProtectionStatus);
401 mData.putUInt32(info->mCompressedSize);
402 mData.putUInt16(info->mThumbFormat);
403 mData.putUInt32(info->mThumbCompressedSize);
404 mData.putUInt32(info->mThumbPixWidth);
405 mData.putUInt32(info->mThumbPixHeight);
406 mData.putUInt32(info->mImagePixWidth);
407 mData.putUInt32(info->mImagePixHeight);
408 mData.putUInt32(info->mImagePixDepth);
409 mData.putUInt32(info->mParent);
410 mData.putUInt16(info->mAssociationType);
411 mData.putUInt32(info->mAssociationDesc);
412 mData.putUInt32(info->mSequenceNumber);
413 mData.putString(info->mName);
414
415 char created[100], modified[100];
416 formatDateTime(info->mDateCreated, created, sizeof(created));
417 formatDateTime(info->mDateModified, modified, sizeof(modified));
418
419 mData.putString(created);
420 mData.putString(modified);
421 if (info->mKeywords)
422 mData.putString(info->mKeywords);
423 else
424 mData.putEmptyString();
425
426 if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400427 MtpResponseCode ret = readResponse();
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400428 if (ret == MTP_RESPONSE_OK) {
429 info->mStorageID = mResponse.getParameter(1);
430 info->mParent = mResponse.getParameter(2);
431 info->mHandle = mResponse.getParameter(3);
432 return info->mHandle;
433 }
434 }
435 return (MtpObjectHandle)-1;
436}
437
438bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
439 Mutex::Autolock autoLock(mMutex);
440
441 int remaining = info->mCompressedSize;
442 mRequest.reset();
443 mRequest.setParameter(1, info->mHandle);
444 if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400445 // send data header
446 writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
447
448 char buffer[65536];
449 while (remaining > 0) {
450 int count = read(srcFD, buffer, sizeof(buffer));
451 if (count > 0) {
Mike Lockwood42d0b792011-01-04 14:48:57 -0500452 int written = mData.write(mRequestOut, buffer, count);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400453 // FIXME check error
454 remaining -= count;
455 } else {
456 break;
457 }
458 }
459 }
460 MtpResponseCode ret = readResponse();
461 return (remaining == 0 && ret == MTP_RESPONSE_OK);
462}
463
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400464bool MtpDevice::deleteObject(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400465 Mutex::Autolock autoLock(mMutex);
466
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400467 mRequest.reset();
468 mRequest.setParameter(1, handle);
469 if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
470 MtpResponseCode ret = readResponse();
471 if (ret == MTP_RESPONSE_OK)
472 return true;
473 }
474 return false;
475}
476
477MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
478 MtpObjectInfo* info = getObjectInfo(handle);
479 if (info)
480 return info->mParent;
481 else
482 return -1;
483}
484
485MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
486 MtpObjectInfo* info = getObjectInfo(handle);
487 if (info)
488 return info->mStorageID;
489 else
490 return -1;
Mike Lockwood3e072b32010-06-10 16:34:20 -0400491}
492
Mike Lockwood98693f62010-12-07 10:58:56 -0800493MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) {
494 Mutex::Autolock autoLock(mMutex);
495
496 mRequest.reset();
497 mRequest.setParameter(1, format);
498 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED))
499 return NULL;
500 if (!readData())
501 return NULL;
502 MtpResponseCode ret = readResponse();
503 if (ret == MTP_RESPONSE_OK) {
504 return mData.getAUInt16();
505 }
506 return NULL;
507
508}
509
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400510MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400511 Mutex::Autolock autoLock(mMutex);
512
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400513 mRequest.reset();
514 mRequest.setParameter(1, code);
515 if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
516 return NULL;
517 if (!readData())
518 return NULL;
519 MtpResponseCode ret = readResponse();
520 if (ret == MTP_RESPONSE_OK) {
521 MtpProperty* property = new MtpProperty;
Mike Lockwoode3e76c42010-09-02 14:57:30 -0400522 property->read(mData);
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400523 return property;
524 }
525 return NULL;
526}
527
Mike Lockwood99e393a2010-12-07 18:53:04 -0800528MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format) {
Mike Lockwood98693f62010-12-07 10:58:56 -0800529 Mutex::Autolock autoLock(mMutex);
530
531 mRequest.reset();
532 mRequest.setParameter(1, code);
Mike Lockwood99e393a2010-12-07 18:53:04 -0800533 mRequest.setParameter(2, format);
Mike Lockwood98693f62010-12-07 10:58:56 -0800534 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROP_DESC))
535 return NULL;
536 if (!readData())
537 return NULL;
538 MtpResponseCode ret = readResponse();
539 if (ret == MTP_RESPONSE_OK) {
540 MtpProperty* property = new MtpProperty;
541 property->read(mData);
542 return property;
543 }
544 return NULL;
545}
546
Mike Lockwood23f1b332010-12-30 15:38:45 -0500547bool MtpDevice::readObject(MtpObjectHandle handle,
548 bool (* callback)(void* data, int offset, int length, void* clientData),
549 int objectSize, void* clientData) {
550 Mutex::Autolock autoLock(mMutex);
551 bool result = false;
552
553 mRequest.reset();
554 mRequest.setParameter(1, handle);
555 if (sendRequest(MTP_OPERATION_GET_OBJECT)
556 && mData.readDataHeader(mRequestIn1)) {
557 uint32_t length = mData.getContainerLength();
558 if (length - MTP_CONTAINER_HEADER_SIZE != objectSize) {
559 LOGE("readObject error objectSize: %d, length: %d",
560 objectSize, length);
561 goto fail;
562 }
563 length -= MTP_CONTAINER_HEADER_SIZE;
564 uint32_t remaining = length;
565 int offset = 0;
566
567 int initialDataLength = 0;
568 void* initialData = mData.getData(initialDataLength);
569 if (initialData) {
570 if (initialDataLength > 0) {
571 if (!callback(initialData, 0, initialDataLength, clientData))
572 goto fail;
573 remaining -= initialDataLength;
574 offset += initialDataLength;
575 }
576 free(initialData);
577 }
578
579 // USB reads greater than 16K don't work
580 char buffer1[16384], buffer2[16384];
581 mRequestIn1->buffer = buffer1;
582 mRequestIn2->buffer = buffer2;
583 struct usb_request* req = mRequestIn1;
584 void* writeBuffer = NULL;
585 int writeLength = 0;
586
587 while (remaining > 0 || writeBuffer) {
588 if (remaining > 0) {
589 // queue up a read request
590 req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
591 if (mData.readDataAsync(req)) {
592 LOGE("readDataAsync failed");
593 goto fail;
594 }
595 } else {
596 req = NULL;
597 }
598
599 if (writeBuffer) {
600 // write previous buffer
601 if (!callback(writeBuffer, offset, writeLength, clientData)) {
602 LOGE("write failed");
603 // wait for pending read before failing
604 if (req)
605 mData.readDataWait(mDevice);
606 goto fail;
607 }
608 offset += writeLength;
609 writeBuffer = NULL;
610 }
611
612 // wait for read to complete
613 if (req) {
614 int read = mData.readDataWait(mDevice);
615 if (read < 0)
616 goto fail;
617
618 if (read > 0) {
619 writeBuffer = req->buffer;
620 writeLength = read;
621 remaining -= read;
622 req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
623 } else {
624 writeBuffer = NULL;
625 }
626 }
627 }
628
629 MtpResponseCode response = readResponse();
630 if (response == MTP_RESPONSE_OK)
631 result = true;
632 }
633
634fail:
635 return result;
636}
637
638
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500639// reads the object's data and writes it to the specified file path
Mike Lockwood27afe3a2010-11-19 13:52:20 -0500640bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500641 LOGD("readObject: %s", destPath);
642 int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC);
643 if (fd < 0) {
644 LOGE("open failed for %s", destPath);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400645 return false;
646 }
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400647
Mike Lockwood27afe3a2010-11-19 13:52:20 -0500648 fchown(fd, getuid(), group);
649 // set permissions
650 int mask = umask(0);
651 fchmod(fd, perm);
652 umask(mask);
653
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500654 Mutex::Autolock autoLock(mMutex);
655 bool result = false;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400656
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500657 mRequest.reset();
658 mRequest.setParameter(1, handle);
659 if (sendRequest(MTP_OPERATION_GET_OBJECT)
Mike Lockwood42d0b792011-01-04 14:48:57 -0500660 && mData.readDataHeader(mRequestIn1)) {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500661 uint32_t length = mData.getContainerLength();
662 if (length < MTP_CONTAINER_HEADER_SIZE)
663 goto fail;
664 length -= MTP_CONTAINER_HEADER_SIZE;
665 uint32_t remaining = length;
666
667 int initialDataLength = 0;
668 void* initialData = mData.getData(initialDataLength);
669 if (initialData) {
670 if (initialDataLength > 0) {
671 if (write(fd, initialData, initialDataLength) != initialDataLength)
672 goto fail;
673 remaining -= initialDataLength;
674 }
675 free(initialData);
676 }
677
678 // USB reads greater than 16K don't work
679 char buffer1[16384], buffer2[16384];
Mike Lockwood42d0b792011-01-04 14:48:57 -0500680 mRequestIn1->buffer = buffer1;
681 mRequestIn2->buffer = buffer2;
682 struct usb_request* req = mRequestIn1;
683 void* writeBuffer = NULL;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500684 int writeLength = 0;
685
686 while (remaining > 0 || writeBuffer) {
687 if (remaining > 0) {
688 // queue up a read request
Mike Lockwood42d0b792011-01-04 14:48:57 -0500689 req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
690 if (mData.readDataAsync(req)) {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500691 LOGE("readDataAsync failed");
692 goto fail;
693 }
694 } else {
Mike Lockwood42d0b792011-01-04 14:48:57 -0500695 req = NULL;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500696 }
697
698 if (writeBuffer) {
699 // write previous buffer
700 if (write(fd, writeBuffer, writeLength) != writeLength) {
701 LOGE("write failed");
702 // wait for pending read before failing
Mike Lockwood42d0b792011-01-04 14:48:57 -0500703 if (req)
704 mData.readDataWait(mDevice);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500705 goto fail;
706 }
707 writeBuffer = NULL;
708 }
709
710 // wait for read to complete
Mike Lockwood42d0b792011-01-04 14:48:57 -0500711 if (req) {
712 int read = mData.readDataWait(mDevice);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500713 if (read < 0)
714 goto fail;
715
Mike Lockwood23f1b332010-12-30 15:38:45 -0500716 if (read > 0) {
717 writeBuffer = req->buffer;
718 writeLength = read;
719 remaining -= read;
720 req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
721 } else {
722 writeBuffer = NULL;
723 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500724 }
725 }
726
727 MtpResponseCode response = readResponse();
728 if (response == MTP_RESPONSE_OK)
729 result = true;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400730 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500731
732fail:
733 ::close(fd);
734 return result;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400735}
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400736
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400737bool MtpDevice::sendRequest(MtpOperationCode operation) {
Mike Lockwoodf43c6412010-07-27 11:50:34 -0400738 LOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
Mike Lockwoodf7454622010-12-09 18:34:18 -0800739 mReceivedResponse = false;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400740 mRequest.setOperationCode(operation);
741 if (mTransactionID > 0)
742 mRequest.setTransactionID(mTransactionID++);
Mike Lockwood42d0b792011-01-04 14:48:57 -0500743 int ret = mRequest.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400744 mRequest.dump();
745 return (ret > 0);
746}
747
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400748bool MtpDevice::sendData() {
Mike Lockwoodf43c6412010-07-27 11:50:34 -0400749 LOGV("sendData\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400750 mData.setOperationCode(mRequest.getOperationCode());
751 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500752 int ret = mData.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400753 mData.dump();
754 return (ret > 0);
755}
756
757bool MtpDevice::readData() {
758 mData.reset();
Mike Lockwood42d0b792011-01-04 14:48:57 -0500759 int ret = mData.read(mRequestIn1);
Mike Lockwoodf43c6412010-07-27 11:50:34 -0400760 LOGV("readData returned %d\n", ret);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400761 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
Mike Lockwoodf7454622010-12-09 18:34:18 -0800762 if (mData.getContainerType() == MTP_CONTAINER_TYPE_RESPONSE) {
763 LOGD("got response packet instead of data packet");
764 // we got a response packet rather than data
765 // copy it to mResponse
766 mResponse.copyFrom(mData);
767 mReceivedResponse = true;
768 return false;
769 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400770 mData.dump();
771 return true;
772 }
773 else {
Mike Lockwoodf43c6412010-07-27 11:50:34 -0400774 LOGV("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400775 return false;
776 }
777}
778
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400779bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
780 mData.setOperationCode(operation);
781 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500782 return (!mData.writeDataHeader(mRequestOut, dataLength));
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400783}
784
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400785MtpResponseCode MtpDevice::readResponse() {
Mike Lockwoodf43c6412010-07-27 11:50:34 -0400786 LOGV("readResponse\n");
Mike Lockwoodf7454622010-12-09 18:34:18 -0800787 if (mReceivedResponse) {
788 mReceivedResponse = false;
789 return mResponse.getResponseCode();
790 }
Mike Lockwood42d0b792011-01-04 14:48:57 -0500791 int ret = mResponse.read(mRequestIn1);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400792 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
793 mResponse.dump();
794 return mResponse.getResponseCode();
Mike Lockwoodf7454622010-12-09 18:34:18 -0800795 } else {
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400796 LOGD("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400797 return -1;
798 }
799}
800
801} // namespace android