1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/gamepad/xbox_data_fetcher_mac.h"
6
7#include <algorithm>
8#include <cmath>
9#include <limits>
10
11#include <CoreFoundation/CoreFoundation.h>
12#include <IOKit/IOCFPlugIn.h>
13#include <IOKit/IOKitLib.h>
14#include <IOKit/usb/IOUSBLib.h>
15#include <IOKit/usb/USB.h>
16
17#include "base/logging.h"
18#include "base/mac/foundation_util.h"
19
20namespace {
21const int kVendorMicrosoft = 0x045e;
22const int kProductXbox360Controller = 0x028e;
23const int kProductXboxOneController = 0x02d1;
24
25const int kXbox360ReadEndpoint = 1;
26const int kXbox360ControlEndpoint = 2;
27
28const int kXboxOneReadEndpoint = 2;
29const int kXboxOneControlEndpoint = 1;
30
31enum {
32  STATUS_MESSAGE_BUTTONS = 0,
33  STATUS_MESSAGE_LED = 1,
34
35  // Apparently this message tells you if the rumble pack is disabled in the
36  // controller. If the rumble pack is disabled, vibration control messages
37  // have no effect.
38  STATUS_MESSAGE_RUMBLE = 3,
39};
40
41enum {
42  XBOX_ONE_STATUS_MESSAGE_BUTTONS = 0x20,
43};
44
45enum {
46  CONTROL_MESSAGE_SET_RUMBLE = 0,
47  CONTROL_MESSAGE_SET_LED = 1,
48};
49
50#pragma pack(push, 1)
51struct Xbox360ButtonData {
52  bool dpad_up    : 1;
53  bool dpad_down  : 1;
54  bool dpad_left  : 1;
55  bool dpad_right : 1;
56
57  bool start             : 1;
58  bool back              : 1;
59  bool stick_left_click  : 1;
60  bool stick_right_click : 1;
61
62  bool bumper_left  : 1;
63  bool bumper_right : 1;
64  bool guide        : 1;
65  bool dummy1       : 1;  // Always 0.
66
67  bool a : 1;
68  bool b : 1;
69  bool x : 1;
70  bool y : 1;
71
72  uint8 trigger_left;
73  uint8 trigger_right;
74
75  int16 stick_left_x;
76  int16 stick_left_y;
77  int16 stick_right_x;
78  int16 stick_right_y;
79
80  // Always 0.
81  uint32 dummy2;
82  uint16 dummy3;
83};
84
85struct XboxOneButtonData {
86  bool sync   : 1;
87  bool dummy1 : 1;  // Always 0.
88  bool start  : 1;
89  bool back   : 1;
90
91  bool a : 1;
92  bool b : 1;
93  bool x : 1;
94  bool y : 1;
95
96  bool dpad_up    : 1;
97  bool dpad_down  : 1;
98  bool dpad_left  : 1;
99  bool dpad_right : 1;
100
101  bool bumper_left       : 1;
102  bool bumper_right      : 1;
103  bool stick_left_click  : 1;
104  bool stick_right_click : 1;
105
106  uint16 trigger_left;
107  uint16 trigger_right;
108
109  int16 stick_left_x;
110  int16 stick_left_y;
111  int16 stick_right_x;
112  int16 stick_right_y;
113};
114#pragma pack(pop)
115
116COMPILE_ASSERT(sizeof(Xbox360ButtonData) == 18, xbox_button_data_wrong_size);
117COMPILE_ASSERT(sizeof(XboxOneButtonData) == 14, xbox_button_data_wrong_size);
118
119// From MSDN:
120// http://msdn.microsoft.com/en-us/library/windows/desktop/ee417001(v=vs.85).aspx#dead_zone
121const int16 kLeftThumbDeadzone = 7849;
122const int16 kRightThumbDeadzone = 8689;
123const uint8 kXbox360TriggerDeadzone = 30;
124const uint16 kXboxOneTriggerMax = 1023;
125const uint16 kXboxOneTriggerDeadzone = 120;
126
127void NormalizeAxis(int16 x,
128                       int16 y,
129                       int16 deadzone,
130                       float* x_out,
131                       float* y_out) {
132  float x_val = x;
133  float y_val = y;
134
135  // Determine how far the stick is pushed.
136  float real_magnitude = std::sqrt(x_val * x_val + y_val * y_val);
137
138  // Check if the controller is outside a circular dead zone.
139  if (real_magnitude > deadzone) {
140    // Clip the magnitude at its expected maximum value.
141    float magnitude = std::min(32767.0f, real_magnitude);
142
143    // Adjust magnitude relative to the end of the dead zone.
144    magnitude -= deadzone;
145
146    // Normalize the magnitude with respect to its expected range giving a
147    // magnitude value of 0.0 to 1.0
148    float ratio = (magnitude / (32767 - deadzone)) / real_magnitude;
149
150    // Y is negated because xbox controllers have an opposite sign from
151    // the 'standard controller' recommendations.
152    *x_out = x_val * ratio;
153    *y_out = -y_val * ratio;
154  } else {
155    // If the controller is in the deadzone zero out the magnitude.
156    *x_out = *y_out = 0.0f;
157  }
158}
159
160float NormalizeTrigger(uint8 value) {
161  return value < kXbox360TriggerDeadzone ? 0 :
162      static_cast<float>(value - kXbox360TriggerDeadzone) /
163          (std::numeric_limits<uint8>::max() - kXbox360TriggerDeadzone);
164}
165
166float NormalizeXboxOneTrigger(uint16 value) {
167  return value < kXboxOneTriggerDeadzone ? 0 :
168      static_cast<float>(value - kXboxOneTriggerDeadzone) /
169          (kXboxOneTriggerMax - kXboxOneTriggerDeadzone);
170}
171
172void NormalizeXbox360ButtonData(const Xbox360ButtonData& data,
173    XboxController::Data* normalized_data) {
174  normalized_data->buttons[0] = data.a;
175  normalized_data->buttons[1] = data.b;
176  normalized_data->buttons[2] = data.x;
177  normalized_data->buttons[3] = data.y;
178  normalized_data->buttons[4] = data.bumper_left;
179  normalized_data->buttons[5] = data.bumper_right;
180  normalized_data->buttons[6] = data.back;
181  normalized_data->buttons[7] = data.start;
182  normalized_data->buttons[8] = data.stick_left_click;
183  normalized_data->buttons[9] = data.stick_right_click;
184  normalized_data->buttons[10] = data.dpad_up;
185  normalized_data->buttons[11] = data.dpad_down;
186  normalized_data->buttons[12] = data.dpad_left;
187  normalized_data->buttons[13] = data.dpad_right;
188  normalized_data->buttons[14] = data.guide;
189  normalized_data->triggers[0] = NormalizeTrigger(data.trigger_left);
190  normalized_data->triggers[1] = NormalizeTrigger(data.trigger_right);
191  NormalizeAxis(data.stick_left_x,
192                data.stick_left_y,
193                kLeftThumbDeadzone,
194                &normalized_data->axes[0],
195                &normalized_data->axes[1]);
196  NormalizeAxis(data.stick_right_x,
197                data.stick_right_y,
198                kRightThumbDeadzone,
199                &normalized_data->axes[2],
200                &normalized_data->axes[3]);
201}
202
203void NormalizeXboxOneButtonData(const XboxOneButtonData& data,
204    XboxController::Data* normalized_data) {
205  normalized_data->buttons[0] = data.a;
206  normalized_data->buttons[1] = data.b;
207  normalized_data->buttons[2] = data.x;
208  normalized_data->buttons[3] = data.y;
209  normalized_data->buttons[4] = data.bumper_left;
210  normalized_data->buttons[5] = data.bumper_right;
211  normalized_data->buttons[6] = data.back;
212  normalized_data->buttons[7] = data.start;
213  normalized_data->buttons[8] = data.stick_left_click;
214  normalized_data->buttons[9] = data.stick_right_click;
215  normalized_data->buttons[10] = data.dpad_up;
216  normalized_data->buttons[11] = data.dpad_down;
217  normalized_data->buttons[12] = data.dpad_left;
218  normalized_data->buttons[13] = data.dpad_right;
219  normalized_data->buttons[14] = data.sync;
220  normalized_data->triggers[0] = NormalizeXboxOneTrigger(data.trigger_left);
221  normalized_data->triggers[1] = NormalizeXboxOneTrigger(data.trigger_right);
222  NormalizeAxis(data.stick_left_x,
223                data.stick_left_y,
224                kLeftThumbDeadzone,
225                &normalized_data->axes[0],
226                &normalized_data->axes[1]);
227  NormalizeAxis(data.stick_right_x,
228                data.stick_right_y,
229                kRightThumbDeadzone,
230                &normalized_data->axes[2],
231                &normalized_data->axes[3]);
232}
233
234}  // namespace
235
236XboxController::XboxController(Delegate* delegate)
237    : device_(NULL),
238      interface_(NULL),
239      device_is_open_(false),
240      interface_is_open_(false),
241      read_buffer_size_(0),
242      led_pattern_(LED_NUM_PATTERNS),
243      location_id_(0),
244      delegate_(delegate),
245      controller_type_(UNKNOWN_CONTROLLER),
246      read_endpoint_(0),
247      control_endpoint_(0) {
248}
249
250XboxController::~XboxController() {
251  if (source_)
252    CFRunLoopSourceInvalidate(source_);
253  if (interface_ && interface_is_open_)
254    (*interface_)->USBInterfaceClose(interface_);
255  if (device_ && device_is_open_)
256    (*device_)->USBDeviceClose(device_);
257}
258
259bool XboxController::OpenDevice(io_service_t service) {
260  IOCFPlugInInterface **plugin;
261  SInt32 score;  // Unused, but required for IOCreatePlugInInterfaceForService.
262  kern_return_t kr =
263      IOCreatePlugInInterfaceForService(service,
264                                        kIOUSBDeviceUserClientTypeID,
265                                        kIOCFPlugInInterfaceID,
266                                        &plugin,
267                                        &score);
268  if (kr != KERN_SUCCESS)
269    return false;
270  base::mac::ScopedIOPluginInterface<IOCFPlugInInterface> plugin_ref(plugin);
271
272  HRESULT res =
273      (*plugin)->QueryInterface(plugin,
274                                CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320),
275                                (LPVOID *)&device_);
276  if (!SUCCEEDED(res) || !device_)
277    return false;
278
279  UInt16 vendor_id;
280  kr = (*device_)->GetDeviceVendor(device_, &vendor_id);
281  if (kr != KERN_SUCCESS || vendor_id != kVendorMicrosoft)
282    return false;
283
284  UInt16 product_id;
285  kr = (*device_)->GetDeviceProduct(device_, &product_id);
286  if (kr != KERN_SUCCESS)
287    return false;
288
289  IOUSBFindInterfaceRequest request;
290  switch (product_id) {
291    case kProductXbox360Controller:
292      controller_type_ = XBOX_360_CONTROLLER;
293      read_endpoint_ = kXbox360ReadEndpoint;
294      control_endpoint_ = kXbox360ControlEndpoint;
295      request.bInterfaceClass = 255;
296      request.bInterfaceSubClass = 93;
297      request.bInterfaceProtocol = 1;
298      request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
299      break;
300    case kProductXboxOneController:
301      controller_type_ = XBOX_ONE_CONTROLLER;
302      read_endpoint_ = kXboxOneReadEndpoint;
303      control_endpoint_ = kXboxOneControlEndpoint;
304      request.bInterfaceClass = 255;
305      request.bInterfaceSubClass = 71;
306      request.bInterfaceProtocol = 208;
307      request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
308      break;
309    default:
310      return false;
311  }
312
313  // Open the device and configure it.
314  kr = (*device_)->USBDeviceOpen(device_);
315  if (kr != KERN_SUCCESS)
316    return false;
317  device_is_open_ = true;
318
319  // Xbox controllers have one configuration option which has configuration
320  // value 1. Try to set it and fail if it couldn't be configured.
321  IOUSBConfigurationDescriptorPtr config_desc;
322  kr = (*device_)->GetConfigurationDescriptorPtr(device_, 0, &config_desc);
323  if (kr != KERN_SUCCESS)
324    return false;
325  kr = (*device_)->SetConfiguration(device_, config_desc->bConfigurationValue);
326  if (kr != KERN_SUCCESS)
327    return false;
328
329  // The device has 4 interfaces. They are as follows:
330  // Protocol 1:
331  //  - Endpoint 1 (in) : Controller events, including button presses.
332  //  - Endpoint 2 (out): Rumble pack and LED control
333  // Protocol 2 has a single endpoint to read from a connected ChatPad device.
334  // Protocol 3 is used by a connected headset device.
335  // The device also has an interface on subclass 253, protocol 10 with no
336  // endpoints.  It is unused.
337  //
338  // We don't currently support the ChatPad or headset, so protocol 1 is the
339  // only protocol we care about.
340  //
341  // For more detail, see
342  // https://github.com/Grumbel/xboxdrv/blob/master/PROTOCOL
343  io_iterator_t iter;
344  kr = (*device_)->CreateInterfaceIterator(device_, &request, &iter);
345  if (kr != KERN_SUCCESS)
346    return false;
347  base::mac::ScopedIOObject<io_iterator_t> iter_ref(iter);
348
349  // There should be exactly one USB interface which matches the requested
350  // settings.
351  io_service_t usb_interface = IOIteratorNext(iter);
352  if (!usb_interface)
353    return false;
354
355  // We need to make an InterfaceInterface to communicate with the device
356  // endpoint. This is the same process as earlier: first make a
357  // PluginInterface from the io_service then make the InterfaceInterface from
358  // that.
359  IOCFPlugInInterface **plugin_interface;
360  kr = IOCreatePlugInInterfaceForService(usb_interface,
361                                         kIOUSBInterfaceUserClientTypeID,
362                                         kIOCFPlugInInterfaceID,
363                                         &plugin_interface,
364                                         &score);
365  if (kr != KERN_SUCCESS || !plugin_interface)
366    return false;
367  base::mac::ScopedIOPluginInterface<IOCFPlugInInterface> interface_ref(
368      plugin_interface);
369
370  // Release the USB interface, and any subsequent interfaces returned by the
371  // iterator. (There shouldn't be any, but in case a future device does
372  // contain more interfaces, this will serve to avoid memory leaks.)
373  do {
374    IOObjectRelease(usb_interface);
375  } while ((usb_interface = IOIteratorNext(iter)));
376
377  // Actually create the interface.
378  res = (*plugin_interface)->QueryInterface(
379      plugin_interface,
380      CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID300),
381      (LPVOID *)&interface_);
382
383  if (!SUCCEEDED(res) || !interface_)
384    return false;
385
386  // Actually open the interface.
387  kr = (*interface_)->USBInterfaceOpen(interface_);
388  if (kr != KERN_SUCCESS)
389    return false;
390  interface_is_open_ = true;
391
392  CFRunLoopSourceRef source_ref;
393  kr = (*interface_)->CreateInterfaceAsyncEventSource(interface_, &source_ref);
394  if (kr != KERN_SUCCESS || !source_ref)
395    return false;
396  source_.reset(source_ref);
397  CFRunLoopAddSource(CFRunLoopGetCurrent(), source_, kCFRunLoopDefaultMode);
398
399  // The interface should have two pipes. Pipe 1 with direction kUSBIn and pipe
400  // 2 with direction kUSBOut. Both pipes should have type kUSBInterrupt.
401  uint8 num_endpoints;
402  kr = (*interface_)->GetNumEndpoints(interface_, &num_endpoints);
403  if (kr != KERN_SUCCESS || num_endpoints < 2)
404    return false;
405
406  for (int i = 1; i <= 2; i++) {
407    uint8 direction;
408    uint8 number;
409    uint8 transfer_type;
410    uint16 max_packet_size;
411    uint8 interval;
412
413    kr = (*interface_)->GetPipeProperties(interface_,
414                                          i,
415                                          &direction,
416                                          &number,
417                                          &transfer_type,
418                                          &max_packet_size,
419                                          &interval);
420    if (kr != KERN_SUCCESS || transfer_type != kUSBInterrupt) {
421      return false;
422    }
423    if (i == read_endpoint_) {
424      if (direction != kUSBIn)
425        return false;
426      read_buffer_.reset(new uint8[max_packet_size]);
427      read_buffer_size_ = max_packet_size;
428      QueueRead();
429    } else if (i == control_endpoint_) {
430      if (direction != kUSBOut)
431        return false;
432      if (controller_type_ == XBOX_ONE_CONTROLLER)
433        WriteXboxOneInit();
434    }
435  }
436
437  // The location ID is unique per controller, and can be used to track
438  // controllers through reconnections (though if a controller is detached from
439  // one USB hub and attached to another, the location ID will change).
440  kr = (*device_)->GetLocationID(device_, &location_id_);
441  if (kr != KERN_SUCCESS)
442    return false;
443
444  return true;
445}
446
447void XboxController::SetLEDPattern(LEDPattern pattern) {
448  led_pattern_ = pattern;
449  const UInt8 length = 3;
450
451  // This buffer will be released in WriteComplete when WritePipeAsync
452  // finishes.
453  UInt8* buffer = new UInt8[length];
454  buffer[0] = static_cast<UInt8>(CONTROL_MESSAGE_SET_LED);
455  buffer[1] = length;
456  buffer[2] = static_cast<UInt8>(pattern);
457  kern_return_t kr = (*interface_)->WritePipeAsync(interface_,
458                                                   control_endpoint_,
459                                                   buffer,
460                                                   (UInt32)length,
461                                                   WriteComplete,
462                                                   buffer);
463  if (kr != KERN_SUCCESS) {
464    delete[] buffer;
465    IOError();
466    return;
467  }
468}
469
470int XboxController::GetVendorId() const {
471  return kVendorMicrosoft;
472}
473
474int XboxController::GetProductId() const {
475  if (controller_type_ == XBOX_360_CONTROLLER)
476    return kProductXbox360Controller;
477  else
478    return kProductXboxOneController;
479}
480
481XboxController::ControllerType XboxController::GetControllerType() const {
482  return controller_type_;
483}
484
485void XboxController::WriteComplete(void* context, IOReturn result, void* arg0) {
486  UInt8* buffer = static_cast<UInt8*>(context);
487  delete[] buffer;
488
489  // Ignoring any errors sending data, because they will usually only occur
490  // when the device is disconnected, in which case it really doesn't matter if
491  // the data got to the controller or not.
492  if (result != kIOReturnSuccess)
493    return;
494}
495
496void XboxController::GotData(void* context, IOReturn result, void* arg0) {
497  size_t bytes_read = reinterpret_cast<size_t>(arg0);
498  XboxController* controller = static_cast<XboxController*>(context);
499
500  if (result != kIOReturnSuccess) {
501    // This will happen if the device was disconnected. The gamepad has
502    // probably been destroyed by a meteorite.
503    controller->IOError();
504    return;
505  }
506
507  if (controller->GetControllerType() == XBOX_360_CONTROLLER)
508    controller->ProcessXbox360Packet(bytes_read);
509  else
510    controller->ProcessXboxOnePacket(bytes_read);
511
512  // Queue up another read.
513  controller->QueueRead();
514}
515
516void XboxController::ProcessXbox360Packet(size_t length) {
517  if (length < 2)
518    return;
519  DCHECK(length <= read_buffer_size_);
520  if (length > read_buffer_size_) {
521    IOError();
522    return;
523  }
524  uint8* buffer = read_buffer_.get();
525
526  if (buffer[1] != length)
527    // Length in packet doesn't match length reported by USB.
528    return;
529
530  uint8 type = buffer[0];
531  buffer += 2;
532  length -= 2;
533  switch (type) {
534    case STATUS_MESSAGE_BUTTONS: {
535      if (length != sizeof(Xbox360ButtonData))
536        return;
537      Xbox360ButtonData* data = reinterpret_cast<Xbox360ButtonData*>(buffer);
538      Data normalized_data;
539      NormalizeXbox360ButtonData(*data, &normalized_data);
540      delegate_->XboxControllerGotData(this, normalized_data);
541      break;
542    }
543    case STATUS_MESSAGE_LED:
544      if (length != 3)
545        return;
546      // The controller sends one of these messages every time the LED pattern
547      // is set, as well as once when it is plugged in.
548      if (led_pattern_ == LED_NUM_PATTERNS && buffer[0] < LED_NUM_PATTERNS)
549        led_pattern_ = static_cast<LEDPattern>(buffer[0]);
550      break;
551    default:
552      // Unknown packet: ignore!
553      break;
554  }
555}
556
557void XboxController::ProcessXboxOnePacket(size_t length) {
558  if (length < 2)
559    return;
560  DCHECK(length <= read_buffer_size_);
561  if (length > read_buffer_size_) {
562    IOError();
563    return;
564  }
565  uint8* buffer = read_buffer_.get();
566
567  uint8 type = buffer[0];
568  buffer += 4;
569  length -= 4;
570  switch (type) {
571    case XBOX_ONE_STATUS_MESSAGE_BUTTONS: {
572      if (length != sizeof(XboxOneButtonData))
573        return;
574      XboxOneButtonData* data = reinterpret_cast<XboxOneButtonData*>(buffer);
575      Data normalized_data;
576      NormalizeXboxOneButtonData(*data, &normalized_data);
577      delegate_->XboxControllerGotData(this, normalized_data);
578      break;
579    }
580    default:
581      // Unknown packet: ignore!
582      break;
583  }
584}
585
586void XboxController::QueueRead() {
587  kern_return_t kr = (*interface_)->ReadPipeAsync(interface_,
588                                                  read_endpoint_,
589                                                  read_buffer_.get(),
590                                                  read_buffer_size_,
591                                                  GotData,
592                                                  this);
593  if (kr != KERN_SUCCESS)
594    IOError();
595}
596
597void XboxController::IOError() {
598  delegate_->XboxControllerError(this);
599}
600
601void XboxController::WriteXboxOneInit() {
602  const UInt8 length = 2;
603
604  // This buffer will be released in WriteComplete when WritePipeAsync
605  // finishes.
606  UInt8* buffer = new UInt8[length];
607  buffer[0] = 0x05;
608  buffer[1] = 0x20;
609  kern_return_t kr = (*interface_)->WritePipeAsync(interface_,
610                                                   control_endpoint_,
611                                                   buffer,
612                                                   (UInt32)length,
613                                                   WriteComplete,
614                                                   buffer);
615  if (kr != KERN_SUCCESS) {
616    delete[] buffer;
617    IOError();
618    return;
619  }
620}
621
622//-----------------------------------------------------------------------------
623
624XboxDataFetcher::XboxDataFetcher(Delegate* delegate)
625    : delegate_(delegate),
626      listening_(false),
627      source_(NULL),
628      port_(NULL) {
629}
630
631XboxDataFetcher::~XboxDataFetcher() {
632  while (!controllers_.empty()) {
633    RemoveController(*controllers_.begin());
634  }
635  UnregisterFromNotifications();
636}
637
638void XboxDataFetcher::DeviceAdded(void* context, io_iterator_t iterator) {
639  DCHECK(context);
640  XboxDataFetcher* fetcher = static_cast<XboxDataFetcher*>(context);
641  io_service_t ref;
642  while ((ref = IOIteratorNext(iterator))) {
643    base::mac::ScopedIOObject<io_service_t> scoped_ref(ref);
644    XboxController* controller = new XboxController(fetcher);
645    if (controller->OpenDevice(ref)) {
646      fetcher->AddController(controller);
647    } else {
648      delete controller;
649    }
650  }
651}
652
653void XboxDataFetcher::DeviceRemoved(void* context, io_iterator_t iterator) {
654  DCHECK(context);
655  XboxDataFetcher* fetcher = static_cast<XboxDataFetcher*>(context);
656  io_service_t ref;
657  while ((ref = IOIteratorNext(iterator))) {
658    base::mac::ScopedIOObject<io_service_t> scoped_ref(ref);
659    base::ScopedCFTypeRef<CFNumberRef> number(
660        base::mac::CFCastStrict<CFNumberRef>(
661            IORegistryEntryCreateCFProperty(ref,
662                                            CFSTR(kUSBDevicePropertyLocationID),
663                                            kCFAllocatorDefault,
664                                            kNilOptions)));
665    UInt32 location_id = 0;
666    CFNumberGetValue(number, kCFNumberSInt32Type, &location_id);
667    fetcher->RemoveControllerByLocationID(location_id);
668  }
669}
670
671bool XboxDataFetcher::RegisterForNotifications() {
672  if (listening_)
673    return true;
674  port_ = IONotificationPortCreate(kIOMasterPortDefault);
675  if (!port_)
676    return false;
677  source_ = IONotificationPortGetRunLoopSource(port_);
678  if (!source_)
679    return false;
680  CFRunLoopAddSource(CFRunLoopGetCurrent(), source_, kCFRunLoopDefaultMode);
681
682  listening_ = true;
683
684  if (!RegisterForDeviceNotifications(
685      kVendorMicrosoft, kProductXboxOneController,
686      &xbox_one_device_added_iter_,
687      &xbox_one_device_removed_iter_))
688    return false;
689
690  if (!RegisterForDeviceNotifications(
691      kVendorMicrosoft, kProductXbox360Controller,
692      &xbox_360_device_added_iter_,
693      &xbox_360_device_removed_iter_))
694    return false;
695
696  return true;
697}
698
699bool XboxDataFetcher::RegisterForDeviceNotifications(
700    int vendor_id,
701    int product_id,
702    base::mac::ScopedIOObject<io_iterator_t>* added_iter,
703    base::mac::ScopedIOObject<io_iterator_t>* removed_iter) {
704  base::ScopedCFTypeRef<CFNumberRef> vendor_cf(CFNumberCreate(
705      kCFAllocatorDefault, kCFNumberSInt32Type, &vendor_id));
706  base::ScopedCFTypeRef<CFNumberRef> product_cf(CFNumberCreate(
707      kCFAllocatorDefault, kCFNumberSInt32Type, &product_id));
708  base::ScopedCFTypeRef<CFMutableDictionaryRef> matching_dict(
709      IOServiceMatching(kIOUSBDeviceClassName));
710  if (!matching_dict)
711    return false;
712  CFDictionarySetValue(matching_dict, CFSTR(kUSBVendorID), vendor_cf);
713  CFDictionarySetValue(matching_dict, CFSTR(kUSBProductID), product_cf);
714
715  // IOServiceAddMatchingNotification() releases the dictionary when it's done.
716  // Retain it before each call to IOServiceAddMatchingNotification to keep
717  // things balanced.
718  CFRetain(matching_dict);
719  io_iterator_t device_added_iter;
720  IOReturn ret;
721  ret = IOServiceAddMatchingNotification(port_,
722                                         kIOFirstMatchNotification,
723                                         matching_dict,
724                                         DeviceAdded,
725                                         this,
726                                         &device_added_iter);
727  added_iter->reset(device_added_iter);
728  if (ret != kIOReturnSuccess) {
729    LOG(ERROR) << "Error listening for Xbox controller add events: " << ret;
730    return false;
731  }
732  DeviceAdded(this, added_iter->get());
733
734  CFRetain(matching_dict);
735  io_iterator_t device_removed_iter;
736  ret = IOServiceAddMatchingNotification(port_,
737                                         kIOTerminatedNotification,
738                                         matching_dict,
739                                         DeviceRemoved,
740                                         this,
741                                         &device_removed_iter);
742  removed_iter->reset(device_removed_iter);
743  if (ret != kIOReturnSuccess) {
744    LOG(ERROR) << "Error listening for Xbox controller remove events: " << ret;
745    return false;
746  }
747  DeviceRemoved(this, removed_iter->get());
748  return true;
749}
750
751void XboxDataFetcher::UnregisterFromNotifications() {
752  if (!listening_)
753    return;
754  listening_ = false;
755  if (source_)
756    CFRunLoopSourceInvalidate(source_);
757  if (port_)
758    IONotificationPortDestroy(port_);
759  port_ = NULL;
760}
761
762XboxController* XboxDataFetcher::ControllerForLocation(UInt32 location_id) {
763  for (std::set<XboxController*>::iterator i = controllers_.begin();
764       i != controllers_.end();
765       ++i) {
766    if ((*i)->location_id() == location_id)
767      return *i;
768  }
769  return NULL;
770}
771
772void XboxDataFetcher::AddController(XboxController* controller) {
773  DCHECK(!ControllerForLocation(controller->location_id()))
774      << "Controller with location ID " << controller->location_id()
775      << " already exists in the set of controllers.";
776  controllers_.insert(controller);
777  delegate_->XboxDeviceAdd(controller);
778}
779
780void XboxDataFetcher::RemoveController(XboxController* controller) {
781  delegate_->XboxDeviceRemove(controller);
782  controllers_.erase(controller);
783  delete controller;
784}
785
786void XboxDataFetcher::RemoveControllerByLocationID(uint32 location_id) {
787  XboxController* controller = NULL;
788  for (std::set<XboxController*>::iterator i = controllers_.begin();
789       i != controllers_.end();
790       ++i) {
791    if ((*i)->location_id() == location_id) {
792      controller = *i;
793      break;
794    }
795  }
796  if (controller)
797    RemoveController(controller);
798}
799
800void XboxDataFetcher::XboxControllerGotData(XboxController* controller,
801                                            const XboxController::Data& data) {
802  delegate_->XboxValueChanged(controller, data);
803}
804
805void XboxDataFetcher::XboxControllerError(XboxController* controller) {
806  RemoveController(controller);
807}
808