blob: c83054001a0c95ca1f95d6b9300069ba0cf6b4f0 [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 Lockwood42d0b792011-01-04 14:48:57 -050032
33struct usb_device;
Mike Lockwood16864ba2010-05-11 17:16:59 -040034
Mike Lockwood7850ef92010-05-14 10:10:36 -040035namespace android {
36
Mike Lockwood80a82ea2010-07-26 20:33:02 -040037static bool isMtpDevice(uint16_t vendor, uint16_t product) {
38 // Sandisk Sansa Fuze
39 if (vendor == 0x0781 && product == 0x74c2)
40 return true;
41 // Samsung YP-Z5
42 if (vendor == 0x04e8 && product == 0x503c)
43 return true;
44 return false;
45}
46
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040047class MtpClientThread : public Thread {
48private:
49 MtpClient* mClient;
50
51public:
52 MtpClientThread(MtpClient* client)
53 : mClient(client)
54 {
55 }
56
57 virtual bool threadLoop() {
58 return mClient->threadLoop();
59 }
60};
61
62
Mike Lockwood5ed68d22010-05-25 19:08:48 -040063MtpClient::MtpClient()
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040064 : mThread(NULL),
65 mUsbHostContext(NULL),
66 mDone(false)
Mike Lockwood16864ba2010-05-11 17:16:59 -040067{
Mike Lockwood16864ba2010-05-11 17:16:59 -040068}
69
70MtpClient::~MtpClient() {
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040071 usb_host_cleanup(mUsbHostContext);
Mike Lockwood16864ba2010-05-11 17:16:59 -040072}
73
Mike Lockwood5ed68d22010-05-25 19:08:48 -040074bool MtpClient::start() {
Mike Lockwood941f1132010-07-20 09:48:35 -040075 Mutex::Autolock autoLock(mMutex);
76
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040077 if (mThread)
Mike Lockwood16864ba2010-05-11 17:16:59 -040078 return true;
Mike Lockwood5ed68d22010-05-25 19:08:48 -040079
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040080 mUsbHostContext = usb_host_init();
81 if (!mUsbHostContext)
Mike Lockwood16864ba2010-05-11 17:16:59 -040082 return false;
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040083
84 mThread = new MtpClientThread(this);
85 mThread->run("MtpClientThread");
Mike Lockwood941f1132010-07-20 09:48:35 -040086 // wait for the thread to do initial device discovery before returning
87 mThreadStartCondition.wait(mMutex);
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040088
Mike Lockwood5ed68d22010-05-25 19:08:48 -040089 return true;
90}
91
Mike Lockwoodcff0ef92010-07-01 11:32:08 -040092void MtpClient::stop() {
93 mDone = true;
94}
95
Mike Lockwood941f1132010-07-20 09:48:35 -040096MtpDevice* MtpClient::getDevice(int id) {
97 for (int i = 0; i < mDeviceList.size(); i++) {
98 MtpDevice* device = mDeviceList[i];
99 if (device->getID() == id)
100 return device;
101 }
102 return NULL;
103}
104
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400105bool MtpClient::usbDeviceAdded(const char *devname) {
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400106 struct usb_descriptor_header* desc;
107 struct usb_descriptor_iter iter;
108
109 struct usb_device *device = usb_device_open(devname);
110 if (!device) {
111 LOGE("usb_device_open failed\n");
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400112 return mDone;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400113 }
114
115 usb_descriptor_iter_init(device, &iter);
116
117 while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
118 if (desc->bDescriptorType == USB_DT_INTERFACE) {
119 struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
120
121 if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
122 interface->bInterfaceSubClass == 1 && // Still Image Capture
123 interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470)
124 {
125 LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
126 usb_device_get_product_name(device));
Mike Lockwood437e9452010-07-20 12:01:36 -0400127 } else if (interface->bInterfaceClass == 0xFF &&
128 interface->bInterfaceSubClass == 0xFF &&
129 interface->bInterfaceProtocol == 0) {
130 char* interfaceName = usb_device_get_string(device, interface->iInterface);
131 if (!interfaceName || strcmp(interfaceName, "MTP"))
132 continue;
133 // Looks like an android style MTP device
134 LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
135 usb_device_get_product_name(device));
136 } else {
Mike Lockwood80a82ea2010-07-26 20:33:02 -0400137 // look for special cased devices based on vendor/product ID
138 // we are doing this mainly for testing purposes
139 uint16_t vendor = usb_device_get_vendor_id(device);
140 uint16_t product = usb_device_get_product_id(device);
141 if (!isMtpDevice(vendor, product)) {
142 // not an MTP or PTP device
143 continue;
144 }
145 // request MTP OS string and descriptor
146 // some music players need to see this before entering MTP mode.
147 char buffer[256];
148 memset(buffer, 0, sizeof(buffer));
149 int ret = usb_device_send_control(device,
150 USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
151 USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
152 0, sizeof(buffer), buffer);
153 printf("usb_device_send_control returned %d errno: %d\n", ret, errno);
154 if (ret > 0) {
155 printf("got MTP string %s\n", buffer);
156 ret = usb_device_send_control(device,
157 USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
158 0, 4, sizeof(buffer), buffer);
159 printf("OS descriptor got %d\n", ret);
160 } else {
161 printf("no MTP string\n");
162 }
Mike Lockwood437e9452010-07-20 12:01:36 -0400163 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400164
Mike Lockwood437e9452010-07-20 12:01:36 -0400165 // if we got here, then we have a likely MTP or PTP device
166
167 // interface should be followed by three endpoints
168 struct usb_endpoint_descriptor *ep;
169 struct usb_endpoint_descriptor *ep_in_desc = NULL;
170 struct usb_endpoint_descriptor *ep_out_desc = NULL;
171 struct usb_endpoint_descriptor *ep_intr_desc = NULL;
172 for (int i = 0; i < 3; i++) {
173 ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
174 if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400175 LOGE("endpoints not found\n");
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400176 return mDone;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400177 }
Mike Lockwood437e9452010-07-20 12:01:36 -0400178 if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
179 if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
180 ep_in_desc = ep;
181 else
182 ep_out_desc = ep;
183 } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
184 ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
185 ep_intr_desc = ep;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400186 }
Mike Lockwood437e9452010-07-20 12:01:36 -0400187 }
188 if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
189 LOGE("endpoints not found\n");
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400190 return mDone;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400191 }
Mike Lockwood437e9452010-07-20 12:01:36 -0400192
Mike Lockwood437e9452010-07-20 12:01:36 -0400193 if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
Mike Lockwood80a82ea2010-07-26 20:33:02 -0400194 LOGE("usb_device_claim_interface failed errno: %d\n", errno);
Mike Lockwood437e9452010-07-20 12:01:36 -0400195 return mDone;
196 }
197
198 MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
Mike Lockwood42d0b792011-01-04 14:48:57 -0500199 ep_in_desc, ep_out_desc, ep_intr_desc);
Mike Lockwood437e9452010-07-20 12:01:36 -0400200 mDeviceList.add(mtpDevice);
201 mtpDevice->initialize();
202 deviceAdded(mtpDevice);
203 return mDone;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400204 }
205 }
206
207 usb_device_close(device);
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400208 return mDone;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400209}
210
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400211bool MtpClient::usbDeviceRemoved(const char *devname) {
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400212 for (int i = 0; i < mDeviceList.size(); i++) {
213 MtpDevice* device = mDeviceList[i];
214 if (!strcmp(devname, device->getDeviceName())) {
215 deviceRemoved(device);
216 mDeviceList.removeAt(i);
217 delete device;
218 LOGD("Camera removed!\n");
219 break;
220 }
221 }
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400222 return mDone;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400223}
224
Mike Lockwood941f1132010-07-20 09:48:35 -0400225bool MtpClient::usbDiscoveryDone() {
226 Mutex::Autolock autoLock(mMutex);
227 mThreadStartCondition.signal();
228 return mDone;
229}
230
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400231bool MtpClient::threadLoop() {
Mike Lockwood941f1132010-07-20 09:48:35 -0400232 usb_host_run(mUsbHostContext, usb_device_added, usb_device_removed, usb_discovery_done, this);
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400233 return false;
234}
235
236int MtpClient::usb_device_added(const char *devname, void* client_data) {
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400237 LOGD("usb_device_added %s\n", devname);
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400238 return ((MtpClient *)client_data)->usbDeviceAdded(devname);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400239}
240
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400241int MtpClient::usb_device_removed(const char *devname, void* client_data) {
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400242 LOGD("usb_device_removed %s\n", devname);
Mike Lockwoodcff0ef92010-07-01 11:32:08 -0400243 return ((MtpClient *)client_data)->usbDeviceRemoved(devname);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400244}
245
Mike Lockwood941f1132010-07-20 09:48:35 -0400246int MtpClient::usb_discovery_done(void* client_data) {
247 LOGD("usb_discovery_done\n");
248 return ((MtpClient *)client_data)->usbDiscoveryDone();
249}
250
Mike Lockwood7850ef92010-05-14 10:10:36 -0400251} // namespace android