blob: b53fe54891d0cfce5655c973697126509d7ba7c8 [file] [log] [blame]
Mike Lockwood16864ba2010-05-11 17:16:59 -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 Lockwood5ed68d22010-05-25 19:08:48 -040017#define LOG_TAG "MtpClient"
Mike Lockwoodb14e5882010-06-29 18:11:52 -040018
19#include "MtpDebug.h"
20#include "MtpClient.h"
21#include "MtpDevice.h"
Mike Lockwood5ed68d22010-05-25 19:08:48 -040022
Mike Lockwood16864ba2010-05-11 17:16:59 -040023#include <stdio.h>
24#include <stdlib.h>
25#include <sys/types.h>
26#include <sys/ioctl.h>
27#include <sys/stat.h>
28#include <fcntl.h>
29#include <errno.h>
30
31#include <usbhost/usbhost.h>
Mike Lockwood5ed68d22010-05-25 19:08:48 -040032#include <linux/version.h>
33#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
34#include <linux/usb/ch9.h>
35#else
36#include <linux/usb_ch9.h>
37#endif
Mike Lockwood16864ba2010-05-11 17:16:59 -040038
Mike Lockwood7850ef92010-05-14 10:10:36 -040039namespace android {
40
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040041class MtpClientThread : public Thread {
42private:
43 MtpClient* mClient;
44
45public:
46 MtpClientThread(MtpClient* client)
47 : mClient(client)
48 {
49 }
50
51 virtual bool threadLoop() {
52 return mClient->threadLoop();
53 }
54};
55
56
Mike Lockwood5ed68d22010-05-25 19:08:48 -040057MtpClient::MtpClient()
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040058 : mThread(NULL),
59 mUsbHostContext(NULL),
60 mDone(false)
Mike Lockwood16864ba2010-05-11 17:16:59 -040061{
Mike Lockwood16864ba2010-05-11 17:16:59 -040062}
63
64MtpClient::~MtpClient() {
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040065 usb_host_cleanup(mUsbHostContext);
Mike Lockwood16864ba2010-05-11 17:16:59 -040066}
67
Mike Lockwood5ed68d22010-05-25 19:08:48 -040068bool MtpClient::start() {
Mike Lockwood941f1132010-07-20 09:48:35 -040069 Mutex::Autolock autoLock(mMutex);
70
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040071 if (mThread)
Mike Lockwood16864ba2010-05-11 17:16:59 -040072 return true;
Mike Lockwood5ed68d22010-05-25 19:08:48 -040073
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040074 mUsbHostContext = usb_host_init();
75 if (!mUsbHostContext)
Mike Lockwood16864ba2010-05-11 17:16:59 -040076 return false;
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040077
78 mThread = new MtpClientThread(this);
79 mThread->run("MtpClientThread");
Mike Lockwood941f1132010-07-20 09:48:35 -040080 // wait for the thread to do initial device discovery before returning
81 mThreadStartCondition.wait(mMutex);
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040082
Mike Lockwood5ed68d22010-05-25 19:08:48 -040083 return true;
84}
85
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040086void MtpClient::stop() {
87 mDone = true;
88}
89
Mike Lockwood941f1132010-07-20 09:48:35 -040090MtpDevice* MtpClient::getDevice(int id) {
91 for (int i = 0; i < mDeviceList.size(); i++) {
92 MtpDevice* device = mDeviceList[i];
93 if (device->getID() == id)
94 return device;
95 }
96 return NULL;
97}
98
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040099bool MtpClient::usbDeviceAdded(const char *devname) {
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400100 struct usb_descriptor_header* desc;
101 struct usb_descriptor_iter iter;
102
103 struct usb_device *device = usb_device_open(devname);
104 if (!device) {
105 LOGE("usb_device_open failed\n");
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400106 return mDone;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400107 }
108
109 usb_descriptor_iter_init(device, &iter);
110
111 while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
112 if (desc->bDescriptorType == USB_DT_INTERFACE) {
113 struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
114
115 if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
116 interface->bInterfaceSubClass == 1 && // Still Image Capture
117 interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470)
118 {
119 LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
120 usb_device_get_product_name(device));
Mike Lockwood437e9452010-07-20 12:01:36 -0400121 } else if (interface->bInterfaceClass == 0xFF &&
122 interface->bInterfaceSubClass == 0xFF &&
123 interface->bInterfaceProtocol == 0) {
124 char* interfaceName = usb_device_get_string(device, interface->iInterface);
125 if (!interfaceName || strcmp(interfaceName, "MTP"))
126 continue;
127 // Looks like an android style MTP device
128 LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
129 usb_device_get_product_name(device));
130 } else {
131 // not an MTP or PTP device
132 continue;
133 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400134
Mike Lockwood437e9452010-07-20 12:01:36 -0400135 // if we got here, then we have a likely MTP or PTP device
136
137 // interface should be followed by three endpoints
138 struct usb_endpoint_descriptor *ep;
139 struct usb_endpoint_descriptor *ep_in_desc = NULL;
140 struct usb_endpoint_descriptor *ep_out_desc = NULL;
141 struct usb_endpoint_descriptor *ep_intr_desc = NULL;
142 for (int i = 0; i < 3; i++) {
143 ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
144 if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400145 LOGE("endpoints not found\n");
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400146 return mDone;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400147 }
Mike Lockwood437e9452010-07-20 12:01:36 -0400148 if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
149 if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
150 ep_in_desc = ep;
151 else
152 ep_out_desc = ep;
153 } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
154 ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
155 ep_intr_desc = ep;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400156 }
Mike Lockwood437e9452010-07-20 12:01:36 -0400157 }
158 if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
159 LOGE("endpoints not found\n");
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400160 return mDone;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400161 }
Mike Lockwood437e9452010-07-20 12:01:36 -0400162
163 struct usb_endpoint *ep_in = usb_endpoint_open(device, ep_in_desc);
164 struct usb_endpoint *ep_out = usb_endpoint_open(device, ep_out_desc);
165 struct usb_endpoint *ep_intr = usb_endpoint_open(device, ep_intr_desc);
166
167 if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
168 LOGE("usb_device_claim_interface failed\n");
169 usb_endpoint_close(ep_in);
170 usb_endpoint_close(ep_out);
171 usb_endpoint_close(ep_intr);
172 return mDone;
173 }
174
175 MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
176 ep_in, ep_out, ep_intr);
177 mDeviceList.add(mtpDevice);
178 mtpDevice->initialize();
179 deviceAdded(mtpDevice);
180 return mDone;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400181 }
182 }
183
184 usb_device_close(device);
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400185 return mDone;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400186}
187
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400188bool MtpClient::usbDeviceRemoved(const char *devname) {
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400189 for (int i = 0; i < mDeviceList.size(); i++) {
190 MtpDevice* device = mDeviceList[i];
191 if (!strcmp(devname, device->getDeviceName())) {
192 deviceRemoved(device);
193 mDeviceList.removeAt(i);
194 delete device;
195 LOGD("Camera removed!\n");
196 break;
197 }
198 }
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400199 return mDone;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400200}
201
Mike Lockwood941f1132010-07-20 09:48:35 -0400202bool MtpClient::usbDiscoveryDone() {
203 Mutex::Autolock autoLock(mMutex);
204 mThreadStartCondition.signal();
205 return mDone;
206}
207
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400208bool MtpClient::threadLoop() {
Mike Lockwood941f1132010-07-20 09:48:35 -0400209 usb_host_run(mUsbHostContext, usb_device_added, usb_device_removed, usb_discovery_done, this);
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400210 return false;
211}
212
213int MtpClient::usb_device_added(const char *devname, void* client_data) {
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400214 LOGD("usb_device_added %s\n", devname);
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400215 return ((MtpClient *)client_data)->usbDeviceAdded(devname);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400216}
217
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400218int MtpClient::usb_device_removed(const char *devname, void* client_data) {
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400219 LOGD("usb_device_removed %s\n", devname);
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400220 return ((MtpClient *)client_data)->usbDeviceRemoved(devname);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400221}
222
Mike Lockwood941f1132010-07-20 09:48:35 -0400223int MtpClient::usb_discovery_done(void* client_data) {
224 LOGD("usb_discovery_done\n");
225 return ((MtpClient *)client_data)->usbDiscoveryDone();
226}
227
Mike Lockwood7850ef92010-05-14 10:10:36 -0400228} // namespace android