blob: 367694b9d6d692bf89989b73424bcf574899671d [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
41MtpDevice::MtpDevice(struct usb_device* device, int interface,
42 struct usb_endpoint *ep_in, struct usb_endpoint *ep_out,
43 struct usb_endpoint *ep_intr)
44 : mDevice(device),
45 mInterface(interface),
46 mEndpointIn(ep_in),
47 mEndpointOut(ep_out),
48 mEndpointIntr(ep_intr),
49 mDeviceInfo(NULL),
50 mID(usb_device_get_unique_id(device)),
51 mSessionID(0),
52 mTransactionID(0)
53{
54}
55
56MtpDevice::~MtpDevice() {
57 close();
Mike Lockwooda6c490b2010-06-05 22:45:01 -040058 for (int i = 0; i < mDeviceProperties.size(); i++)
59 delete mDeviceProperties[i];
Mike Lockwood5ed68d22010-05-25 19:08:48 -040060}
61
62void MtpDevice::initialize() {
63 openSession();
64 mDeviceInfo = getDeviceInfo();
65 if (mDeviceInfo) {
66 mDeviceInfo->print();
Mike Lockwooda6c490b2010-06-05 22:45:01 -040067
68 if (mDeviceInfo->mDeviceProperties) {
69 int count = mDeviceInfo->mDeviceProperties->size();
70 for (int i = 0; i < count; i++) {
71 MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
72 MtpProperty* property = getDevicePropDesc(propCode);
73 if (property) {
74 property->print();
75 mDeviceProperties.push(property);
76 }
77 }
78 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -040079 }
80}
81
82void MtpDevice::close() {
83 if (mDevice) {
84 usb_device_release_interface(mDevice, mInterface);
85 usb_device_close(mDevice);
86 mDevice = NULL;
87 }
88}
89
90const char* MtpDevice::getDeviceName() {
91 if (mDevice)
92 return usb_device_get_name(mDevice);
93 else
94 return "???";
95}
96
97bool MtpDevice::openSession() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -040098 Mutex::Autolock autoLock(mMutex);
99
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400100 mSessionID = 0;
101 mTransactionID = 0;
102 MtpSessionID newSession = 1;
103 mRequest.reset();
104 mRequest.setParameter(1, newSession);
105 if (!sendRequest(MTP_OPERATION_OPEN_SESSION))
106 return false;
107 MtpResponseCode ret = readResponse();
108 if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN)
109 newSession = mResponse.getParameter(1);
110 else if (ret != MTP_RESPONSE_OK)
111 return false;
112
113 mSessionID = newSession;
114 mTransactionID = 1;
115 return true;
116}
117
118bool MtpDevice::closeSession() {
119 // FIXME
120 return true;
121}
122
123MtpDeviceInfo* MtpDevice::getDeviceInfo() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400124 Mutex::Autolock autoLock(mMutex);
125
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400126 mRequest.reset();
127 if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
128 return NULL;
129 if (!readData())
130 return NULL;
131 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400132 if (ret == MTP_RESPONSE_OK) {
133 MtpDeviceInfo* info = new MtpDeviceInfo;
134 info->read(mData);
135 return info;
136 }
137 return NULL;
138}
139
140MtpStorageIDList* MtpDevice::getStorageIDs() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400141 Mutex::Autolock autoLock(mMutex);
142
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400143 mRequest.reset();
144 if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
145 return NULL;
146 if (!readData())
147 return NULL;
148 MtpResponseCode ret = readResponse();
149 if (ret == MTP_RESPONSE_OK) {
150 return mData.getAUInt32();
151 }
152 return NULL;
153}
154
155MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400156 Mutex::Autolock autoLock(mMutex);
157
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400158 mRequest.reset();
159 mRequest.setParameter(1, storageID);
160 if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
161 return NULL;
162 if (!readData())
163 return NULL;
164 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400165 if (ret == MTP_RESPONSE_OK) {
166 MtpStorageInfo* info = new MtpStorageInfo(storageID);
167 info->read(mData);
168 return info;
169 }
170 return NULL;
171}
172
173MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
174 MtpObjectFormat format, MtpObjectHandle parent) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400175 Mutex::Autolock autoLock(mMutex);
176
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400177 mRequest.reset();
178 mRequest.setParameter(1, storageID);
179 mRequest.setParameter(2, format);
180 mRequest.setParameter(3, parent);
181 if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES))
182 return NULL;
183 if (!readData())
184 return NULL;
185 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400186 if (ret == MTP_RESPONSE_OK) {
187 return mData.getAUInt32();
188 }
189 return NULL;
190}
191
192MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400193 Mutex::Autolock autoLock(mMutex);
194
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400195 // FIXME - we might want to add some caching here
196
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400197 mRequest.reset();
198 mRequest.setParameter(1, handle);
199 if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO))
200 return NULL;
201 if (!readData())
202 return NULL;
203 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400204 if (ret == MTP_RESPONSE_OK) {
205 MtpObjectInfo* info = new MtpObjectInfo(handle);
206 info->read(mData);
207 return info;
208 }
209 return NULL;
210}
211
Mike Lockwood3e072b32010-06-10 16:34:20 -0400212void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400213 Mutex::Autolock autoLock(mMutex);
214
Mike Lockwood3e072b32010-06-10 16:34:20 -0400215 mRequest.reset();
216 mRequest.setParameter(1, handle);
217 if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
218 MtpResponseCode ret = readResponse();
219 if (ret == MTP_RESPONSE_OK) {
220 return mData.getData(outLength);
221 }
222 }
223 outLength = 0;
224 return NULL;
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400225}
Mike Lockwood3e072b32010-06-10 16:34:20 -0400226
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400227MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) {
228 Mutex::Autolock autoLock(mMutex);
229
230 mRequest.reset();
231 MtpObjectHandle parent = info->mParent;
232 if (parent == 0)
233 parent = MTP_PARENT_ROOT;
234
235 mRequest.setParameter(1, info->mStorageID);
236 mRequest.setParameter(2, info->mParent);
237
238 mData.putUInt32(info->mStorageID);
239 mData.putUInt16(info->mFormat);
240 mData.putUInt16(info->mProtectionStatus);
241 mData.putUInt32(info->mCompressedSize);
242 mData.putUInt16(info->mThumbFormat);
243 mData.putUInt32(info->mThumbCompressedSize);
244 mData.putUInt32(info->mThumbPixWidth);
245 mData.putUInt32(info->mThumbPixHeight);
246 mData.putUInt32(info->mImagePixWidth);
247 mData.putUInt32(info->mImagePixHeight);
248 mData.putUInt32(info->mImagePixDepth);
249 mData.putUInt32(info->mParent);
250 mData.putUInt16(info->mAssociationType);
251 mData.putUInt32(info->mAssociationDesc);
252 mData.putUInt32(info->mSequenceNumber);
253 mData.putString(info->mName);
254
255 char created[100], modified[100];
256 formatDateTime(info->mDateCreated, created, sizeof(created));
257 formatDateTime(info->mDateModified, modified, sizeof(modified));
258
259 mData.putString(created);
260 mData.putString(modified);
261 if (info->mKeywords)
262 mData.putString(info->mKeywords);
263 else
264 mData.putEmptyString();
265
266 if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) {
267 printf("MTP_OPERATION_SEND_OBJECT_INFO sent\n");
268 MtpResponseCode ret = readResponse();
269 printf("sendObjectInfo response: %04X\n", ret);
270 if (ret == MTP_RESPONSE_OK) {
271 info->mStorageID = mResponse.getParameter(1);
272 info->mParent = mResponse.getParameter(2);
273 info->mHandle = mResponse.getParameter(3);
274 return info->mHandle;
275 }
276 }
277 return (MtpObjectHandle)-1;
278}
279
280bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
281 Mutex::Autolock autoLock(mMutex);
282
283 int remaining = info->mCompressedSize;
284 mRequest.reset();
285 mRequest.setParameter(1, info->mHandle);
286 if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
287 printf("MTP_OPERATION_SEND_OBJECT sent\n");
288 // send data header
289 writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
290
291 char buffer[65536];
292 while (remaining > 0) {
293 int count = read(srcFD, buffer, sizeof(buffer));
294 if (count > 0) {
295 int written = mData.write(mEndpointOut, buffer, count);
296 printf("wrote %d\n", written);
297 // FIXME check error
298 remaining -= count;
299 } else {
300 break;
301 }
302 }
303 }
304 MtpResponseCode ret = readResponse();
305 return (remaining == 0 && ret == MTP_RESPONSE_OK);
306}
307
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400308bool MtpDevice::deleteObject(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400309 Mutex::Autolock autoLock(mMutex);
310
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400311 mRequest.reset();
312 mRequest.setParameter(1, handle);
313 if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
314 MtpResponseCode ret = readResponse();
315 if (ret == MTP_RESPONSE_OK)
316 return true;
317 }
318 return false;
319}
320
321MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
322 MtpObjectInfo* info = getObjectInfo(handle);
323 if (info)
324 return info->mParent;
325 else
326 return -1;
327}
328
329MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
330 MtpObjectInfo* info = getObjectInfo(handle);
331 if (info)
332 return info->mStorageID;
333 else
334 return -1;
Mike Lockwood3e072b32010-06-10 16:34:20 -0400335}
336
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400337MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400338 Mutex::Autolock autoLock(mMutex);
339
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400340 mRequest.reset();
341 mRequest.setParameter(1, code);
342 if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
343 return NULL;
344 if (!readData())
345 return NULL;
346 MtpResponseCode ret = readResponse();
347 if (ret == MTP_RESPONSE_OK) {
348 MtpProperty* property = new MtpProperty;
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400349 property->read(mData, true);
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400350 return property;
351 }
352 return NULL;
353}
354
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400355class ReadObjectThread : public Thread {
356private:
357 MtpDevice* mDevice;
358 MtpObjectHandle mHandle;
359 int mObjectSize;
360 void* mInitialData;
361 int mInitialDataLength;
362 int mFD;
363
364public:
365 ReadObjectThread(MtpDevice* device, MtpObjectHandle handle, int objectSize)
366 : mDevice(device),
367 mHandle(handle),
368 mObjectSize(objectSize),
369 mInitialData(NULL),
370 mInitialDataLength(0)
371 {
372 }
373
374 virtual ~ReadObjectThread() {
375 if (mFD >= 0)
376 close(mFD);
377 free(mInitialData);
378 }
379
380 // returns file descriptor
381 int init() {
382 mDevice->mRequest.reset();
383 mDevice->mRequest.setParameter(1, mHandle);
384 if (mDevice->sendRequest(MTP_OPERATION_GET_OBJECT)
385 && mDevice->mData.readDataHeader(mDevice->mEndpointIn)) {
386
387 // mData will contain header and possibly the beginning of the object data
388 mInitialData = mDevice->mData.getData(mInitialDataLength);
389
390 // create a pipe for the client to read from
391 int pipefd[2];
392 if (pipe(pipefd) < 0) {
393 LOGE("pipe failed (%s)", strerror(errno));
394 return -1;
395 }
396
397 mFD = pipefd[1];
398 return pipefd[0];
399 } else {
400 return -1;
401 }
402 }
403
404 virtual bool threadLoop() {
405 int remaining = mObjectSize;
406 if (mInitialData) {
407 write(mFD, mInitialData, mInitialDataLength);
408 remaining -= mInitialDataLength;
409 free(mInitialData);
410 mInitialData = NULL;
411 }
412
413 char buffer[65536];
414 while (remaining > 0) {
415 int readSize = (remaining > sizeof(buffer) ? sizeof(buffer) : remaining);
416 int count = mDevice->mData.readData(mDevice->mEndpointIn, buffer, readSize);
417 int written;
418 if (count >= 0) {
419 int written = write(mFD, buffer, count);
420 // FIXME check error
421 remaining -= count;
422 } else {
423 break;
424 }
425 }
426
427 MtpResponseCode ret = mDevice->readResponse();
428 mDevice->mMutex.unlock();
429 return false;
430 }
431};
432
433 // returns the file descriptor for a pipe to read the object's data
434int MtpDevice::readObject(MtpObjectHandle handle, int objectSize) {
435 mMutex.lock();
436
437 ReadObjectThread* thread = new ReadObjectThread(this, handle, objectSize);
438 int fd = thread->init();
439 if (fd < 0) {
440 delete thread;
441 mMutex.unlock();
442 } else {
443 thread->run("ReadObjectThread");
444 }
445 return fd;
446}
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400447
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400448bool MtpDevice::sendRequest(MtpOperationCode operation) {
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400449 LOGD("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400450 mRequest.setOperationCode(operation);
451 if (mTransactionID > 0)
452 mRequest.setTransactionID(mTransactionID++);
453 int ret = mRequest.write(mEndpointOut);
454 mRequest.dump();
455 return (ret > 0);
456}
457
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400458bool MtpDevice::sendData() {
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400459 LOGD("sendData\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400460 mData.setOperationCode(mRequest.getOperationCode());
461 mData.setTransactionID(mRequest.getTransactionID());
462 int ret = mData.write(mEndpointOut);
463 mData.dump();
464 return (ret > 0);
465}
466
467bool MtpDevice::readData() {
468 mData.reset();
469 int ret = mData.read(mEndpointIn);
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400470 LOGD("readData returned %d\n", ret);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400471 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
472 mData.dump();
473 return true;
474 }
475 else {
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400476 LOGD("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400477 return false;
478 }
479}
480
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400481bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
482 mData.setOperationCode(operation);
483 mData.setTransactionID(mRequest.getTransactionID());
484 return (!mData.writeDataHeader(mEndpointOut, dataLength));
485}
486
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400487MtpResponseCode MtpDevice::readResponse() {
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400488 LOGD("readResponse\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400489 int ret = mResponse.read(mEndpointIn);
490 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
491 mResponse.dump();
492 return mResponse.getResponseCode();
493 }
494 else {
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400495 LOGD("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400496 return -1;
497 }
498}
499
500} // namespace android