Home | History | Annotate | Download | only in gamepad
      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 
     20 namespace {
     21 const int kVendorMicrosoft = 0x045e;
     22 const int kProductXbox360Controller = 0x028e;
     23 const int kProductXboxOneController = 0x02d1;
     24 
     25 const int kXbox360ReadEndpoint = 1;
     26 const int kXbox360ControlEndpoint = 2;
     27 
     28 const int kXboxOneReadEndpoint = 2;
     29 const int kXboxOneControlEndpoint = 1;
     30 
     31 enum {
     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 
     41 enum {
     42   XBOX_ONE_STATUS_MESSAGE_BUTTONS = 0x20,
     43 };
     44 
     45 enum {
     46   CONTROL_MESSAGE_SET_RUMBLE = 0,
     47   CONTROL_MESSAGE_SET_LED = 1,
     48 };
     49 
     50 #pragma pack(push, 1)
     51 struct 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 
     85 struct 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 
    116 COMPILE_ASSERT(sizeof(Xbox360ButtonData) == 18, xbox_button_data_wrong_size);
    117 COMPILE_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
    121 const int16 kLeftThumbDeadzone = 7849;
    122 const int16 kRightThumbDeadzone = 8689;
    123 const uint8 kXbox360TriggerDeadzone = 30;
    124 const uint16 kXboxOneTriggerMax = 1023;
    125 const uint16 kXboxOneTriggerDeadzone = 120;
    126 
    127 void 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 
    160 float NormalizeTrigger(uint8 value) {
    161   return value < kXbox360TriggerDeadzone ? 0 :
    162       static_cast<float>(value - kXbox360TriggerDeadzone) /
    163           (std::numeric_limits<uint8>::max() - kXbox360TriggerDeadzone);
    164 }
    165 
    166 float NormalizeXboxOneTrigger(uint16 value) {
    167   return value < kXboxOneTriggerDeadzone ? 0 :
    168       static_cast<float>(value - kXboxOneTriggerDeadzone) /
    169           (kXboxOneTriggerMax - kXboxOneTriggerDeadzone);
    170 }
    171 
    172 void 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 
    203 void 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 
    236 XboxController::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 
    250 XboxController::~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 
    259 bool 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 
    447 void 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 
    470 int XboxController::GetVendorId() const {
    471   return kVendorMicrosoft;
    472 }
    473 
    474 int XboxController::GetProductId() const {
    475   if (controller_type_ == XBOX_360_CONTROLLER)
    476     return kProductXbox360Controller;
    477   else
    478     return kProductXboxOneController;
    479 }
    480 
    481 XboxController::ControllerType XboxController::GetControllerType() const {
    482   return controller_type_;
    483 }
    484 
    485 void 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 
    496 void 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 
    516 void 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 
    557 void 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 
    586 void 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 
    597 void XboxController::IOError() {
    598   delegate_->XboxControllerError(this);
    599 }
    600 
    601 void 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 
    624 XboxDataFetcher::XboxDataFetcher(Delegate* delegate)
    625     : delegate_(delegate),
    626       listening_(false),
    627       source_(NULL),
    628       port_(NULL) {
    629 }
    630 
    631 XboxDataFetcher::~XboxDataFetcher() {
    632   while (!controllers_.empty()) {
    633     RemoveController(*controllers_.begin());
    634   }
    635   UnregisterFromNotifications();
    636 }
    637 
    638 void 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 
    653 void 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 
    671 bool 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 
    699 bool 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 
    751 void 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 
    762 XboxController* 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 
    772 void 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 
    780 void XboxDataFetcher::RemoveController(XboxController* controller) {
    781   delegate_->XboxDeviceRemove(controller);
    782   controllers_.erase(controller);
    783   delete controller;
    784 }
    785 
    786 void 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 
    800 void XboxDataFetcher::XboxControllerGotData(XboxController* controller,
    801                                             const XboxController::Data& data) {
    802   delegate_->XboxValueChanged(controller, data);
    803 }
    804 
    805 void XboxDataFetcher::XboxControllerError(XboxController* controller) {
    806   RemoveController(controller);
    807 }
    808