MtpDevice.cpp revision b14e588bec4d5e39e61b020b5b575f2ce555d316
1/*
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
17#define LOG_TAG "MtpDevice"
18
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"
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <sys/types.h>
30#include <sys/ioctl.h>
31#include <sys/stat.h>
32#include <fcntl.h>
33#include <errno.h>
34
35#include <usbhost/usbhost.h>
36
37namespace android {
38
39MtpDevice::MtpDevice(struct usb_device* device, int interface,
40            struct usb_endpoint *ep_in, struct usb_endpoint *ep_out,
41            struct usb_endpoint *ep_intr)
42    :   mDevice(device),
43        mInterface(interface),
44        mEndpointIn(ep_in),
45        mEndpointOut(ep_out),
46        mEndpointIntr(ep_intr),
47        mDeviceInfo(NULL),
48        mID(usb_device_get_unique_id(device)),
49        mSessionID(0),
50        mTransactionID(0)
51{
52}
53
54MtpDevice::~MtpDevice() {
55    close();
56    for (int i = 0; i < mDeviceProperties.size(); i++)
57        delete mDeviceProperties[i];
58}
59
60void MtpDevice::initialize() {
61    openSession();
62    mDeviceInfo = getDeviceInfo();
63    if (mDeviceInfo) {
64        mDeviceInfo->print();
65
66        if (mDeviceInfo->mDeviceProperties) {
67            int count = mDeviceInfo->mDeviceProperties->size();
68            for (int i = 0; i < count; i++) {
69                MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
70                MtpProperty* property = getDevicePropDesc(propCode);
71                if (property) {
72                    property->print();
73                    mDeviceProperties.push(property);
74                }
75            }
76        }
77    }
78}
79
80void MtpDevice::close() {
81    if (mDevice) {
82        usb_device_release_interface(mDevice, mInterface);
83        usb_device_close(mDevice);
84        mDevice = NULL;
85    }
86}
87
88const char* MtpDevice::getDeviceName() {
89    if (mDevice)
90        return usb_device_get_name(mDevice);
91    else
92        return "???";
93}
94
95bool MtpDevice::openSession() {
96    mSessionID = 0;
97    mTransactionID = 0;
98    MtpSessionID newSession = 1;
99    mRequest.reset();
100    mRequest.setParameter(1, newSession);
101    if (!sendRequest(MTP_OPERATION_OPEN_SESSION))
102        return false;
103    MtpResponseCode ret = readResponse();
104    if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN)
105        newSession = mResponse.getParameter(1);
106    else if (ret != MTP_RESPONSE_OK)
107        return false;
108
109    mSessionID = newSession;
110    mTransactionID = 1;
111    return true;
112}
113
114bool MtpDevice::closeSession() {
115    // FIXME
116    return true;
117}
118
119MtpDeviceInfo* MtpDevice::getDeviceInfo() {
120    mRequest.reset();
121    if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
122        return NULL;
123    if (!readData())
124        return NULL;
125    MtpResponseCode ret = readResponse();
126    if (ret == MTP_RESPONSE_OK) {
127        MtpDeviceInfo* info = new MtpDeviceInfo;
128        info->read(mData);
129        return info;
130    }
131    return NULL;
132}
133
134MtpStorageIDList* MtpDevice::getStorageIDs() {
135    mRequest.reset();
136    if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
137        return NULL;
138    if (!readData())
139        return NULL;
140    MtpResponseCode ret = readResponse();
141    if (ret == MTP_RESPONSE_OK) {
142        return mData.getAUInt32();
143    }
144    return NULL;
145}
146
147MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
148    mRequest.reset();
149    mRequest.setParameter(1, storageID);
150    if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
151        return NULL;
152    if (!readData())
153        return NULL;
154    MtpResponseCode ret = readResponse();
155    if (ret == MTP_RESPONSE_OK) {
156        MtpStorageInfo* info = new MtpStorageInfo(storageID);
157        info->read(mData);
158        return info;
159    }
160    return NULL;
161}
162
163MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
164            MtpObjectFormat format, MtpObjectHandle parent) {
165    mRequest.reset();
166    mRequest.setParameter(1, storageID);
167    mRequest.setParameter(2, format);
168    mRequest.setParameter(3, parent);
169    if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES))
170        return NULL;
171    if (!readData())
172        return NULL;
173    MtpResponseCode ret = readResponse();
174    if (ret == MTP_RESPONSE_OK) {
175        return mData.getAUInt32();
176    }
177    return NULL;
178}
179
180MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
181    // FIXME - we might want to add some caching here
182
183    mRequest.reset();
184    mRequest.setParameter(1, handle);
185    if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO))
186        return NULL;
187    if (!readData())
188        return NULL;
189    MtpResponseCode ret = readResponse();
190    if (ret == MTP_RESPONSE_OK) {
191        MtpObjectInfo* info = new MtpObjectInfo(handle);
192        info->read(mData);
193        return info;
194    }
195    return NULL;
196}
197
198void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
199    mRequest.reset();
200    mRequest.setParameter(1, handle);
201    if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
202        MtpResponseCode ret = readResponse();
203        if (ret == MTP_RESPONSE_OK) {
204            return mData.getData(outLength);
205        }
206    }
207    outLength = 0;
208    return NULL;
209}
210
211bool MtpDevice::deleteObject(MtpObjectHandle handle) {
212    mRequest.reset();
213    mRequest.setParameter(1, handle);
214    if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
215        MtpResponseCode ret = readResponse();
216        if (ret == MTP_RESPONSE_OK)
217            return true;
218    }
219    return false;
220}
221
222MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
223    MtpObjectInfo* info = getObjectInfo(handle);
224    if (info)
225        return info->mParent;
226    else
227        return -1;
228}
229
230MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
231    MtpObjectInfo* info = getObjectInfo(handle);
232    if (info)
233        return info->mStorageID;
234    else
235        return -1;
236}
237
238MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
239    mRequest.reset();
240    mRequest.setParameter(1, code);
241    if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
242        return NULL;
243    if (!readData())
244        return NULL;
245    MtpResponseCode ret = readResponse();
246    if (ret == MTP_RESPONSE_OK) {
247        MtpProperty* property = new MtpProperty;
248        property->read(mData);
249        return property;
250    }
251    return NULL;
252}
253
254
255bool MtpDevice::sendRequest(MtpOperationCode operation) {
256    LOGD("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
257    mRequest.setOperationCode(operation);
258    if (mTransactionID > 0)
259        mRequest.setTransactionID(mTransactionID++);
260    int ret = mRequest.write(mEndpointOut);
261    mRequest.dump();
262    return (ret > 0);
263}
264
265bool MtpDevice::sendData(MtpOperationCode operation) {
266    LOGD("sendData\n");
267    mData.setOperationCode(mRequest.getOperationCode());
268    mData.setTransactionID(mRequest.getTransactionID());
269    int ret = mData.write(mEndpointOut);
270    mData.dump();
271    return (ret > 0);
272}
273
274bool MtpDevice::readData() {
275    mData.reset();
276    int ret = mData.read(mEndpointIn);
277    LOGD("readData returned %d\n", ret);
278    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
279        mData.dump();
280        return true;
281    }
282    else {
283        LOGD("readResponse failed\n");
284        return false;
285    }
286}
287
288MtpResponseCode MtpDevice::readResponse() {
289    LOGD("readResponse\n");
290    int ret = mResponse.read(mEndpointIn);
291    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
292        mResponse.dump();
293        return mResponse.getResponseCode();
294    }
295    else {
296        LOGD("readResponse failed\n");
297        return -1;
298    }
299}
300
301}  // namespace android
302