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