blob: ceb6a4302404d4392adb2a2bccf32d22da080020 [file] [log] [blame]
Mike Lockwood56118b52010-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 Lockwood755fd612010-05-25 19:08:48 -040017#define LOG_TAG "MtpClient"
Mike Lockwood3e6616d2010-06-29 18:11:52 -040018
19#include "MtpDebug.h"
20#include "MtpClient.h"
21#include "MtpDevice.h"
Mike Lockwood755fd612010-05-25 19:08:48 -040022
Mike Lockwood56118b52010-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 Lockwood755fd612010-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 Lockwood56118b52010-05-11 17:16:59 -040038
Mike Lockwood8d3257a2010-05-14 10:10:36 -040039namespace android {
40
Mike Lockwood86187092010-07-26 20:33:02 -040041static 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
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -040051class MtpClientThread : public Thread {
52private:
53 MtpClient* mClient;
54
55public:
56 MtpClientThread(MtpClient* client)
57 : mClient(client)
58 {
59 }
60
61 virtual bool threadLoop() {
62 return mClient->threadLoop();
63 }
64};
65
66
Mike Lockwood755fd612010-05-25 19:08:48 -040067MtpClient::MtpClient()
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -040068 : mThread(NULL),
69 mUsbHostContext(NULL),
70 mDone(false)
Mike Lockwood56118b52010-05-11 17:16:59 -040071{
Mike Lockwood56118b52010-05-11 17:16:59 -040072}
73
74MtpClient::~MtpClient() {
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -040075 usb_host_cleanup(mUsbHostContext);
Mike Lockwood56118b52010-05-11 17:16:59 -040076}
77
Mike Lockwood755fd612010-05-25 19:08:48 -040078bool MtpClient::start() {
Mike Lockwood108505b2010-07-20 09:48:35 -040079 Mutex::Autolock autoLock(mMutex);
80
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -040081 if (mThread)
Mike Lockwood56118b52010-05-11 17:16:59 -040082 return true;
Mike Lockwood755fd612010-05-25 19:08:48 -040083
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -040084 mUsbHostContext = usb_host_init();
85 if (!mUsbHostContext)
Mike Lockwood56118b52010-05-11 17:16:59 -040086 return false;
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -040087
88 mThread = new MtpClientThread(this);
89 mThread->run("MtpClientThread");
Mike Lockwood108505b2010-07-20 09:48:35 -040090 // wait for the thread to do initial device discovery before returning
91 mThreadStartCondition.wait(mMutex);
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -040092
Mike Lockwood755fd612010-05-25 19:08:48 -040093 return true;
94}
95
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -040096void MtpClient::stop() {
97 mDone = true;
98}
99
Mike Lockwood108505b2010-07-20 09:48:35 -0400100MtpDevice* MtpClient::getDevice(int id) {
101 for (int i = 0; i < mDeviceList.size(); i++) {
102 MtpDevice* device = mDeviceList[i];
103 if (device->getID() == id)
104 return device;
105 }
106 return NULL;
107}
108
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -0400109bool MtpClient::usbDeviceAdded(const char *devname) {
Mike Lockwood755fd612010-05-25 19:08:48 -0400110 struct usb_descriptor_header* desc;
111 struct usb_descriptor_iter iter;
112
113 struct usb_device *device = usb_device_open(devname);
114 if (!device) {
115 LOGE("usb_device_open failed\n");
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -0400116 return mDone;
Mike Lockwood755fd612010-05-25 19:08:48 -0400117 }
118
119 usb_descriptor_iter_init(device, &iter);
120
121 while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
122 if (desc->bDescriptorType == USB_DT_INTERFACE) {
123 struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
124
125 if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
126 interface->bInterfaceSubClass == 1 && // Still Image Capture
127 interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470)
128 {
129 LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
130 usb_device_get_product_name(device));
Mike Lockwood3c51d6a2010-07-20 12:01:36 -0400131 } else if (interface->bInterfaceClass == 0xFF &&
132 interface->bInterfaceSubClass == 0xFF &&
133 interface->bInterfaceProtocol == 0) {
134 char* interfaceName = usb_device_get_string(device, interface->iInterface);
135 if (!interfaceName || strcmp(interfaceName, "MTP"))
136 continue;
137 // Looks like an android style MTP device
138 LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
139 usb_device_get_product_name(device));
140 } else {
Mike Lockwood86187092010-07-26 20:33:02 -0400141 // look for special cased devices based on vendor/product ID
142 // we are doing this mainly for testing purposes
143 uint16_t vendor = usb_device_get_vendor_id(device);
144 uint16_t product = usb_device_get_product_id(device);
145 if (!isMtpDevice(vendor, product)) {
146 // not an MTP or PTP device
147 continue;
148 }
149 // request MTP OS string and descriptor
150 // some music players need to see this before entering MTP mode.
151 char buffer[256];
152 memset(buffer, 0, sizeof(buffer));
153 int ret = usb_device_send_control(device,
154 USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
155 USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
156 0, sizeof(buffer), buffer);
157 printf("usb_device_send_control returned %d errno: %d\n", ret, errno);
158 if (ret > 0) {
159 printf("got MTP string %s\n", buffer);
160 ret = usb_device_send_control(device,
161 USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
162 0, 4, sizeof(buffer), buffer);
163 printf("OS descriptor got %d\n", ret);
164 } else {
165 printf("no MTP string\n");
166 }
Mike Lockwood3c51d6a2010-07-20 12:01:36 -0400167 }
Mike Lockwood755fd612010-05-25 19:08:48 -0400168
Mike Lockwood3c51d6a2010-07-20 12:01:36 -0400169 // if we got here, then we have a likely MTP or PTP device
170
171 // interface should be followed by three endpoints
172 struct usb_endpoint_descriptor *ep;
173 struct usb_endpoint_descriptor *ep_in_desc = NULL;
174 struct usb_endpoint_descriptor *ep_out_desc = NULL;
175 struct usb_endpoint_descriptor *ep_intr_desc = NULL;
176 for (int i = 0; i < 3; i++) {
177 ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
178 if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
Mike Lockwood755fd612010-05-25 19:08:48 -0400179 LOGE("endpoints not found\n");
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -0400180 return mDone;
Mike Lockwood755fd612010-05-25 19:08:48 -0400181 }
Mike Lockwood3c51d6a2010-07-20 12:01:36 -0400182 if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
183 if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
184 ep_in_desc = ep;
185 else
186 ep_out_desc = ep;
187 } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
188 ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
189 ep_intr_desc = ep;
Mike Lockwood755fd612010-05-25 19:08:48 -0400190 }
Mike Lockwood3c51d6a2010-07-20 12:01:36 -0400191 }
192 if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
193 LOGE("endpoints not found\n");
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -0400194 return mDone;
Mike Lockwood755fd612010-05-25 19:08:48 -0400195 }
Mike Lockwood3c51d6a2010-07-20 12:01:36 -0400196
197 struct usb_endpoint *ep_in = usb_endpoint_open(device, ep_in_desc);
198 struct usb_endpoint *ep_out = usb_endpoint_open(device, ep_out_desc);
199 struct usb_endpoint *ep_intr = usb_endpoint_open(device, ep_intr_desc);
200
201 if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
Mike Lockwood86187092010-07-26 20:33:02 -0400202 LOGE("usb_device_claim_interface failed errno: %d\n", errno);
Mike Lockwood3c51d6a2010-07-20 12:01:36 -0400203 usb_endpoint_close(ep_in);
204 usb_endpoint_close(ep_out);
205 usb_endpoint_close(ep_intr);
206 return mDone;
207 }
208
209 MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
210 ep_in, ep_out, ep_intr);
211 mDeviceList.add(mtpDevice);
212 mtpDevice->initialize();
213 deviceAdded(mtpDevice);
214 return mDone;
Mike Lockwood755fd612010-05-25 19:08:48 -0400215 }
216 }
217
218 usb_device_close(device);
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -0400219 return mDone;
Mike Lockwood755fd612010-05-25 19:08:48 -0400220}
221
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -0400222bool MtpClient::usbDeviceRemoved(const char *devname) {
Mike Lockwood755fd612010-05-25 19:08:48 -0400223 for (int i = 0; i < mDeviceList.size(); i++) {
224 MtpDevice* device = mDeviceList[i];
225 if (!strcmp(devname, device->getDeviceName())) {
226 deviceRemoved(device);
227 mDeviceList.removeAt(i);
228 delete device;
229 LOGD("Camera removed!\n");
230 break;
231 }
232 }
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -0400233 return mDone;
Mike Lockwood56118b52010-05-11 17:16:59 -0400234}
235
Mike Lockwood108505b2010-07-20 09:48:35 -0400236bool MtpClient::usbDiscoveryDone() {
237 Mutex::Autolock autoLock(mMutex);
238 mThreadStartCondition.signal();
239 return mDone;
240}
241
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -0400242bool MtpClient::threadLoop() {
Mike Lockwood108505b2010-07-20 09:48:35 -0400243 usb_host_run(mUsbHostContext, usb_device_added, usb_device_removed, usb_discovery_done, this);
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -0400244 return false;
245}
246
247int MtpClient::usb_device_added(const char *devname, void* client_data) {
Mike Lockwood755fd612010-05-25 19:08:48 -0400248 LOGD("usb_device_added %s\n", devname);
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -0400249 return ((MtpClient *)client_data)->usbDeviceAdded(devname);
Mike Lockwood755fd612010-05-25 19:08:48 -0400250}
251
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -0400252int MtpClient::usb_device_removed(const char *devname, void* client_data) {
Mike Lockwood755fd612010-05-25 19:08:48 -0400253 LOGD("usb_device_removed %s\n", devname);
Mike Lockwoodd0e1a9f2010-07-01 11:32:08 -0400254 return ((MtpClient *)client_data)->usbDeviceRemoved(devname);
Mike Lockwood56118b52010-05-11 17:16:59 -0400255}
256
Mike Lockwood108505b2010-07-20 09:48:35 -0400257int MtpClient::usb_discovery_done(void* client_data) {
258 LOGD("usb_discovery_done\n");
259 return ((MtpClient *)client_data)->usbDiscoveryDone();
260}
261
Mike Lockwood8d3257a2010-05-14 10:10:36 -0400262} // namespace android