• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
NormalizeAxis(int16 x,int16 y,int16 deadzone,float * x_out,float * y_out)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 
NormalizeTrigger(uint8 value)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 
NormalizeXboxOneTrigger(uint16 value)166 float NormalizeXboxOneTrigger(uint16 value) {
167   return value < kXboxOneTriggerDeadzone ? 0 :
168       static_cast<float>(value - kXboxOneTriggerDeadzone) /
169           (kXboxOneTriggerMax - kXboxOneTriggerDeadzone);
170 }
171 
NormalizeXbox360ButtonData(const Xbox360ButtonData & data,XboxController::Data * normalized_data)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 
NormalizeXboxOneButtonData(const XboxOneButtonData & data,XboxController::Data * normalized_data)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 
XboxController(Delegate * delegate)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 
~XboxController()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 
OpenDevice(io_service_t service)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 
SetLEDPattern(LEDPattern pattern)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 
GetVendorId() const470 int XboxController::GetVendorId() const {
471   return kVendorMicrosoft;
472 }
473 
GetProductId() const474 int XboxController::GetProductId() const {
475   if (controller_type_ == XBOX_360_CONTROLLER)
476     return kProductXbox360Controller;
477   else
478     return kProductXboxOneController;
479 }
480 
GetControllerType() const481 XboxController::ControllerType XboxController::GetControllerType() const {
482   return controller_type_;
483 }
484 
WriteComplete(void * context,IOReturn result,void * arg0)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 
GotData(void * context,IOReturn result,void * arg0)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 
ProcessXbox360Packet(size_t length)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 
ProcessXboxOnePacket(size_t length)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 
QueueRead()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 
IOError()597 void XboxController::IOError() {
598   delegate_->XboxControllerError(this);
599 }
600 
WriteXboxOneInit()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 
XboxDataFetcher(Delegate * delegate)624 XboxDataFetcher::XboxDataFetcher(Delegate* delegate)
625     : delegate_(delegate),
626       listening_(false),
627       source_(NULL),
628       port_(NULL) {
629 }
630 
~XboxDataFetcher()631 XboxDataFetcher::~XboxDataFetcher() {
632   while (!controllers_.empty()) {
633     RemoveController(*controllers_.begin());
634   }
635   UnregisterFromNotifications();
636 }
637 
DeviceAdded(void * context,io_iterator_t iterator)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 
DeviceRemoved(void * context,io_iterator_t iterator)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 
RegisterForNotifications()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 
RegisterForDeviceNotifications(int vendor_id,int product_id,base::mac::ScopedIOObject<io_iterator_t> * added_iter,base::mac::ScopedIOObject<io_iterator_t> * removed_iter)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 
UnregisterFromNotifications()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 
ControllerForLocation(UInt32 location_id)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 
AddController(XboxController * controller)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 
RemoveController(XboxController * controller)780 void XboxDataFetcher::RemoveController(XboxController* controller) {
781   delegate_->XboxDeviceRemove(controller);
782   controllers_.erase(controller);
783   delete controller;
784 }
785 
RemoveControllerByLocationID(uint32 location_id)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 
XboxControllerGotData(XboxController * controller,const XboxController::Data & data)800 void XboxDataFetcher::XboxControllerGotData(XboxController* controller,
801                                             const XboxController::Data& data) {
802   delegate_->XboxValueChanged(controller, data);
803 }
804 
XboxControllerError(XboxController * controller)805 void XboxDataFetcher::XboxControllerError(XboxController* controller) {
806   RemoveController(controller);
807 }
808