blob: 610eebfb230eb2936f13d3b9c5a86e49dcfb84fc [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
Anatol Pomazau5ae3f932012-02-28 07:21:08 -080012 * the documentation and/or other materials provided with the
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080013 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
Anatol Pomazau5ae3f932012-02-28 07:21:08 -080022 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080023 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
Elliott Hughesfbcb93a2015-06-24 13:28:24 -070029#include <inttypes.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080030#include <stdio.h>
31#include <CoreFoundation/CoreFoundation.h>
32#include <IOKit/IOKitLib.h>
33#include <IOKit/IOCFPlugIn.h>
34#include <IOKit/usb/IOUSBLib.h>
35#include <IOKit/IOMessage.h>
36#include <mach/mach_port.h>
37
David Pursell0b156632015-10-30 11:22:01 -070038#include <memory>
39
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080040#include "usb.h"
41
42
43/*
44 * Internal helper functions and associated definitions.
45 */
46
47#if TRACE_USB
48#define WARN(x...) fprintf(stderr, x)
49#else
50#define WARN(x...)
51#endif
52
53#define ERR(x...) fprintf(stderr, "ERROR: " x)
54
55/** An open usb device */
56struct usb_handle
57{
58 int success;
59 ifc_match_func callback;
60 usb_ifc_info info;
Anatol Pomazau5ae3f932012-02-28 07:21:08 -080061
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080062 UInt8 bulkIn;
63 UInt8 bulkOut;
Eran Messeribe0ab0a2018-11-10 23:56:19 +000064 IOUSBInterfaceInterface500** interface;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080065 unsigned int zero_mask;
66};
67
Aaron Wisneracf78d42018-07-26 10:56:09 -050068class OsxUsbTransport : public UsbTransport {
David Pursell0b156632015-10-30 11:22:01 -070069 public:
Aaron Wisneracf78d42018-07-26 10:56:09 -050070 // A timeout of 0 is blocking
71 OsxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout = 0)
72 : handle_(std::move(handle)), ms_timeout_(ms_timeout) {}
David Anderson03de6452018-09-04 14:32:54 -070073 ~OsxUsbTransport() override;
David Pursell0b156632015-10-30 11:22:01 -070074
75 ssize_t Read(void* data, size_t len) override;
76 ssize_t Write(const void* data, size_t len) override;
77 int Close() override;
Aaron Wisneracf78d42018-07-26 10:56:09 -050078 int Reset() override;
David Pursell0b156632015-10-30 11:22:01 -070079
80 private:
81 std::unique_ptr<usb_handle> handle_;
Aaron Wisneracf78d42018-07-26 10:56:09 -050082 const uint32_t ms_timeout_;
David Pursell0b156632015-10-30 11:22:01 -070083
84 DISALLOW_COPY_AND_ASSIGN(OsxUsbTransport);
85};
86
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080087/** Try out all the interfaces and see if there's a match. Returns 0 on
88 * success, -1 on failure. */
Eran Messeribe0ab0a2018-11-10 23:56:19 +000089static int try_interfaces(IOUSBDeviceInterface500** dev, usb_handle* handle) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080090 IOReturn kr;
91 IOUSBFindInterfaceRequest request;
92 io_iterator_t iterator;
93 io_service_t usbInterface;
94 IOCFPlugInInterface **plugInInterface;
Eran Messeribe0ab0a2018-11-10 23:56:19 +000095 IOUSBInterfaceInterface500** interface = NULL;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080096 HRESULT result;
97 SInt32 score;
98 UInt8 interfaceNumEndpoints;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080099
Josh Gaof806a3c2017-08-18 18:25:44 -0700100 request.bInterfaceClass = 0xff;
101 request.bInterfaceSubClass = 0x42;
102 request.bInterfaceProtocol = 0x03;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800103 request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
104
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800105 // Get an iterator for the interfaces on the device
106 kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
107
108 if (kr != 0) {
Josh Gao16f992e2019-05-10 11:28:38 -0700109 WARN("Couldn't create a device interface iterator: (%08x)\n", kr);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800110 return -1;
111 }
112
113 while ((usbInterface = IOIteratorNext(iterator))) {
114 // Create an intermediate plugin
115 kr = IOCreatePlugInInterfaceForService(
116 usbInterface,
117 kIOUSBInterfaceUserClientTypeID,
118 kIOCFPlugInInterfaceID,
119 &plugInInterface,
120 &score);
121
122 // No longer need the usbInterface object now that we have the plugin
123 (void) IOObjectRelease(usbInterface);
124
125 if ((kr != 0) || (!plugInInterface)) {
126 WARN("Unable to create plugin (%08x)\n", kr);
127 continue;
128 }
129
130 // Now create the interface interface for the interface
Eran Messeribe0ab0a2018-11-10 23:56:19 +0000131 result = (*plugInInterface)
132 ->QueryInterface(plugInInterface,
133 CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID500),
134 (LPVOID*)&interface);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800135
136 // No longer need the intermediate plugin
137 (*plugInInterface)->Release(plugInInterface);
138
139 if (result || !interface) {
140 ERR("Couldn't create interface interface: (%08x)\n",
141 (unsigned int) result);
142 // continue so we can try the next interface
143 continue;
144 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800145
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800146 /*
147 * Now open the interface. This will cause the pipes
148 * associated with the endpoints in the interface descriptor
149 * to be instantiated.
150 */
151
152 /*
153 * TODO: Earlier comments here indicated that it was a bad
154 * idea to just open any interface, because opening "mass
155 * storage endpoints" is bad. However, the only way to find
156 * out if an interface does bulk in or out is to open it, and
157 * the framework in this application wants to be told about
158 * bulk in / out before deciding whether it actually wants to
159 * use the interface. Maybe something needs to be done about
160 * this situation.
161 */
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800162
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800163 kr = (*interface)->USBInterfaceOpen(interface);
164
165 if (kr != 0) {
166 WARN("Could not open interface: (%08x)\n", kr);
167 (void) (*interface)->Release(interface);
168 // continue so we can try the next interface
169 continue;
170 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800171
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800172 // Get the number of endpoints associated with this interface.
173 kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
174
175 if (kr != 0) {
176 ERR("Unable to get number of endpoints: (%08x)\n", kr);
177 goto next_interface;
178 }
179
180 // Get interface class, subclass and protocol
181 if ((*interface)->GetInterfaceClass(interface, &handle->info.ifc_class) != 0 ||
182 (*interface)->GetInterfaceSubClass(interface, &handle->info.ifc_subclass) != 0 ||
183 (*interface)->GetInterfaceProtocol(interface, &handle->info.ifc_protocol) != 0)
184 {
185 ERR("Unable to get interface class, subclass and protocol\n");
186 goto next_interface;
187 }
188
189 handle->info.has_bulk_in = 0;
190 handle->info.has_bulk_out = 0;
191
192 // Iterate over the endpoints for this interface and see if there
193 // are any that do bulk in/out.
Elliott Hughes2d4f8522015-08-13 15:01:18 -0700194 for (UInt8 endpoint = 1; endpoint <= interfaceNumEndpoints; endpoint++) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800195 UInt8 transferType;
196 UInt16 maxPacketSize;
197 UInt8 interval;
198 UInt8 number;
199 UInt8 direction;
200
201 kr = (*interface)->GetPipeProperties(interface, endpoint,
202 &direction,
203 &number, &transferType, &maxPacketSize, &interval);
204
205 if (kr == 0) {
206 if (transferType != kUSBBulk) {
207 continue;
208 }
209
210 if (direction == kUSBIn) {
211 handle->info.has_bulk_in = 1;
212 handle->bulkIn = endpoint;
213 } else if (direction == kUSBOut) {
214 handle->info.has_bulk_out = 1;
215 handle->bulkOut = endpoint;
216 }
217
218 if (handle->info.ifc_protocol == 0x01) {
219 handle->zero_mask = maxPacketSize - 1;
220 }
221 } else {
Elliott Hughes2ae8d2e2015-08-07 10:49:36 -0700222 ERR("could not get pipe properties for endpoint %u (%08x)\n", endpoint, kr);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800223 }
224
225 if (handle->info.has_bulk_in && handle->info.has_bulk_out) {
226 break;
227 }
228 }
229
230 if (handle->callback(&handle->info) == 0) {
231 handle->interface = interface;
232 handle->success = 1;
233
234 /*
235 * Clear both the endpoints, because it has been observed
236 * that the Mac may otherwise (incorrectly) start out with
237 * them in bad state.
238 */
239
240 if (handle->info.has_bulk_in) {
241 kr = (*interface)->ClearPipeStallBothEnds(interface,
242 handle->bulkIn);
243 if (kr != 0) {
Nick Pelly286d50f2010-07-22 10:59:55 -0700244 ERR("could not clear input pipe; result %x, ignoring...\n", kr);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800245 }
246 }
247
248 if (handle->info.has_bulk_out) {
249 kr = (*interface)->ClearPipeStallBothEnds(interface,
250 handle->bulkOut);
251 if (kr != 0) {
Nick Pelly286d50f2010-07-22 10:59:55 -0700252 ERR("could not clear output pipe; result %x, ignoring....\n", kr);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800253 }
254 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800255
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800256 return 0;
257 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800258
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800259next_interface:
260 (*interface)->USBInterfaceClose(interface);
261 (*interface)->Release(interface);
262 }
263
264 return 0;
265}
266
267/** Try out the given device and see if there's a match. Returns 0 on
268 * success, -1 on failure.
269 */
270static int try_device(io_service_t device, usb_handle *handle) {
271 kern_return_t kr;
272 IOCFPlugInInterface **plugin = NULL;
Eran Messeribe0ab0a2018-11-10 23:56:19 +0000273 IOUSBDeviceInterface500** dev = NULL;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800274 SInt32 score;
275 HRESULT result;
276 UInt8 serialIndex;
Scott Anderson13081c62012-04-06 12:39:30 -0700277 UInt32 locationId;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800278
279 // Create an intermediate plugin.
280 kr = IOCreatePlugInInterfaceForService(device,
281 kIOUSBDeviceUserClientTypeID,
282 kIOCFPlugInInterfaceID,
283 &plugin, &score);
284
285 if ((kr != 0) || (plugin == NULL)) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800286 goto error;
287 }
288
289 // Now create the device interface.
Eran Messeribe0ab0a2018-11-10 23:56:19 +0000290 result = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500),
291 (LPVOID*)&dev);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800292 if ((result != 0) || (dev == NULL)) {
293 ERR("Couldn't create a device interface (%08x)\n", (int) result);
294 goto error;
295 }
296
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800297 /*
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800298 * We don't need the intermediate interface after the device interface
299 * is created.
300 */
301 IODestroyPlugInInterface(plugin);
302
303 // So, we have a device, finally. Grab its vitals.
304
305 kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
306 if (kr != 0) {
307 ERR("GetDeviceVendor");
308 goto error;
309 }
310
311 kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product);
312 if (kr != 0) {
313 ERR("GetDeviceProduct");
314 goto error;
315 }
316
317 kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class);
318 if (kr != 0) {
319 ERR("GetDeviceClass");
320 goto error;
321 }
322
323 kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass);
324 if (kr != 0) {
325 ERR("GetDeviceSubClass");
326 goto error;
327 }
328
329 kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol);
330 if (kr != 0) {
331 ERR("GetDeviceProtocol");
332 goto error;
333 }
334
Scott Anderson13081c62012-04-06 12:39:30 -0700335 kr = (*dev)->GetLocationID(dev, &locationId);
336 if (kr != 0) {
337 ERR("GetLocationId");
338 goto error;
339 }
Ying Wang42a809b2014-08-14 15:50:13 -0700340 snprintf(handle->info.device_path, sizeof(handle->info.device_path),
341 "usb:%" PRIu32 "X", (unsigned int)locationId);
Scott Anderson13081c62012-04-06 12:39:30 -0700342
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800343 kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
344
345 if (serialIndex > 0) {
346 IOUSBDevRequest req;
347 UInt16 buffer[256];
348
349 req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
350 req.bRequest = kUSBRqGetDescriptor;
351 req.wValue = (kUSBStringDesc << 8) | serialIndex;
mgrossc8406532011-07-07 17:08:10 -0700352 //language ID (en-us) for serial number string
353 req.wIndex = 0x0409;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800354 req.pData = buffer;
355 req.wLength = sizeof(buffer);
356 kr = (*dev)->DeviceRequest(dev, &req);
357
358 if (kr == kIOReturnSuccess && req.wLenDone > 0) {
359 int i, count;
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800360
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800361 // skip first word, and copy the rest to the serial string, changing shorts to bytes.
362 count = (req.wLenDone - 1) / 2;
363 for (i = 0; i < count; i++)
364 handle->info.serial_number[i] = buffer[i + 1];
365 handle->info.serial_number[i] = 0;
366 }
367 } else {
368 // device has no serial number
369 handle->info.serial_number[0] = 0;
370 }
David Anderson4e058ca2020-05-28 04:39:37 +0000371 handle->info.interface[0] = 0;
Elliott Hughesb4add9b2009-10-06 18:07:49 -0700372 handle->info.writable = 1;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800373
374 if (try_interfaces(dev, handle)) {
375 goto error;
376 }
377
378 (*dev)->Release(dev);
379 return 0;
380
381 error:
382
383 if (dev != NULL) {
384 (*dev)->Release(dev);
385 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800386
387 return -1;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800388}
389
390
391/** Initializes the USB system. Returns 0 on success, -1 on error. */
David Pursell0b156632015-10-30 11:22:01 -0700392static int init_usb(ifc_match_func callback, std::unique_ptr<usb_handle>* handle) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800393 int ret = -1;
394 CFMutableDictionaryRef matchingDict;
395 kern_return_t result;
396 io_iterator_t iterator;
397 usb_handle h;
398
399 h.success = 0;
400 h.callback = callback;
401
402 /*
403 * Create our matching dictionary to find appropriate devices.
404 * IOServiceAddMatchingNotification consumes the reference, so we
405 * do not need to release it.
406 */
407 matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
408
409 if (matchingDict == NULL) {
410 ERR("Couldn't create USB matching dictionary.\n");
411 return -1;
412 }
413
414 result = IOServiceGetMatchingServices(
415 kIOMasterPortDefault, matchingDict, &iterator);
416
417 if (result != 0) {
418 ERR("Could not create iterator.");
419 return -1;
420 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800421
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800422 for (;;) {
423 if (! IOIteratorIsValid(iterator)) {
424 /*
425 * Apple documentation advises resetting the iterator if
426 * it should become invalid during iteration.
427 */
428 IOIteratorReset(iterator);
429 continue;
430 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800431
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800432 io_service_t device = IOIteratorNext(iterator);
433
434 if (device == 0) {
435 break;
436 }
437
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800438 if (try_device(device, &h) != 0) {
439 IOObjectRelease(device);
Josh Gaof806a3c2017-08-18 18:25:44 -0700440 continue;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800441 }
442
443 if (h.success) {
David Pursell0b156632015-10-30 11:22:01 -0700444 handle->reset(new usb_handle);
445 memcpy(handle->get(), &h, sizeof(usb_handle));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800446 ret = 0;
447 break;
448 }
449
450 IOObjectRelease(device);
451 }
452
453 IOObjectRelease(iterator);
454
455 return ret;
456}
457
458
459
460/*
461 * Definitions of this file's public functions.
462 */
463
Aaron Wisneracf78d42018-07-26 10:56:09 -0500464UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
David Pursell0b156632015-10-30 11:22:01 -0700465 std::unique_ptr<usb_handle> handle;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800466
467 if (init_usb(callback, &handle) < 0) {
468 /* Something went wrong initializing USB. */
David Pursell0b156632015-10-30 11:22:01 -0700469 return nullptr;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800470 }
471
Aaron Wisneracf78d42018-07-26 10:56:09 -0500472 return new OsxUsbTransport(std::move(handle), timeout_ms);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800473}
474
David Anderson03de6452018-09-04 14:32:54 -0700475OsxUsbTransport::~OsxUsbTransport() {
476 Close();
477}
478
David Pursell0b156632015-10-30 11:22:01 -0700479int OsxUsbTransport::Close() {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800480 /* TODO: Something better here? */
481 return 0;
482}
483
Aaron Wisneracf78d42018-07-26 10:56:09 -0500484/*
485 TODO: this SHOULD be easy to do with ResetDevice() from IOUSBDeviceInterface.
486 However to perform operations that manipulate the state of the device, you must
487 claim ownership of the device with USBDeviceOpenSeize(). However, this operation
488 always fails with kIOReturnExclusiveAccess.
489 It seems that the kext com.apple.driver.usb.AppleUSBHostCompositeDevice
490 always loads and claims ownership of the device and refuses to give it up.
491*/
492int OsxUsbTransport::Reset() {
493 ERR("USB reset is currently unsupported on osx\n");
494 return -1;
495}
496
David Pursell0b156632015-10-30 11:22:01 -0700497ssize_t OsxUsbTransport::Read(void* data, size_t len) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800498 IOReturn result;
499 UInt32 numBytes = len;
500
501 if (len == 0) {
502 return 0;
503 }
504
David Pursell0b156632015-10-30 11:22:01 -0700505 if (handle_ == nullptr) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800506 return -1;
507 }
508
David Pursell0b156632015-10-30 11:22:01 -0700509 if (handle_->interface == nullptr) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800510 ERR("usb_read interface was null\n");
511 return -1;
512 }
513
David Pursell0b156632015-10-30 11:22:01 -0700514 if (handle_->bulkIn == 0) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800515 ERR("bulkIn endpoint not assigned\n");
516 return -1;
517 }
518
Aaron Wisneracf78d42018-07-26 10:56:09 -0500519 if (!ms_timeout_) {
520 result = (*handle_->interface)
521 ->ReadPipe(handle_->interface, handle_->bulkIn, data, &numBytes);
522 } else {
523 result = (*handle_->interface)
524 ->ReadPipeTO(handle_->interface, handle_->bulkIn, data, &numBytes,
525 ms_timeout_, ms_timeout_);
526 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800527
528 if (result == 0) {
529 return (int) numBytes;
530 } else {
531 ERR("usb_read failed with status %x\n", result);
532 }
533
534 return -1;
535}
536
David Pursell0b156632015-10-30 11:22:01 -0700537ssize_t OsxUsbTransport::Write(const void* data, size_t len) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800538 IOReturn result;
539
540 if (len == 0) {
541 return 0;
542 }
543
David Pursell0b156632015-10-30 11:22:01 -0700544 if (handle_ == NULL) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800545 return -1;
546 }
547
David Pursell0b156632015-10-30 11:22:01 -0700548 if (handle_->interface == NULL) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800549 ERR("usb_write interface was null\n");
550 return -1;
551 }
552
David Pursell0b156632015-10-30 11:22:01 -0700553 if (handle_->bulkOut == 0) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800554 ERR("bulkOut endpoint not assigned\n");
555 return -1;
556 }
557
Jeff Brownb6406372010-05-21 13:20:47 -0700558#if 0
David Pursell0b156632015-10-30 11:22:01 -0700559 result = (*handle_->interface)->WritePipe(
560 handle_->interface, handle_->bulkOut, (void *)data, len);
Jeff Brownb6406372010-05-21 13:20:47 -0700561#else
562 /* Attempt to work around crashes in the USB driver that may be caused
563 * by trying to write too much data at once. The kernel IOCopyMapper
564 * panics if a single iovmAlloc needs more than half of its mapper pages.
565 */
566 const int maxLenToSend = 1048576; // 1 MiB
567 int lenRemaining = len;
568 result = 0;
569 while (lenRemaining > 0) {
570 int lenToSend = lenRemaining > maxLenToSend
571 ? maxLenToSend : lenRemaining;
572
Aaron Wisneracf78d42018-07-26 10:56:09 -0500573 if (!ms_timeout_) { // blocking
574 result = (*handle_->interface)
575 ->WritePipe(handle_->interface, handle_->bulkOut, (void*)data,
576 lenToSend);
577 } else {
578 result = (*handle_->interface)
579 ->WritePipeTO(handle_->interface, handle_->bulkOut, (void*)data,
580 lenToSend, ms_timeout_, ms_timeout_);
581 }
582
Jeff Brownb6406372010-05-21 13:20:47 -0700583 if (result != 0) break;
584
585 lenRemaining -= lenToSend;
586 data = (const char*)data + lenToSend;
587 }
588#endif
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800589
590 #if 0
David Pursell0b156632015-10-30 11:22:01 -0700591 if ((result == 0) && (handle_->zero_mask)) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800592 /* we need 0-markers and our transfer */
David Pursell0b156632015-10-30 11:22:01 -0700593 if(!(len & handle_->zero_mask)) {
594 result = (*handle_->interface)->WritePipe(
595 handle_->interface, handle_->bulkOut, (void *)data, 0);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800596 }
597 }
598 #endif
599
600 if (result != 0) {
601 ERR("usb_write failed with status %x\n", result);
602 return -1;
603 }
604
605 return len;
606}