From 6e1c71aefd6f638b0e27f93bcf80269bd85bd4fd Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Thu, 11 May 2017 09:12:44 -1000 Subject: [PATCH] Working Dualshock output reports on macOS; fix runaway loop --- include/boo/inputdev/DualshockPad.hpp | 10 ++--- lib/inputdev/CFPointer.hpp | 9 ++++ lib/inputdev/DualshockPad.cpp | 50 ++++++++++++--------- lib/inputdev/HIDDeviceIOKit.cpp | 64 +++++++++------------------ lib/inputdev/HIDDeviceUdev.cpp | 50 ++++++--------------- lib/inputdev/HIDDeviceWinUSB.cpp | 52 ++++++++++------------ 6 files changed, 101 insertions(+), 134 deletions(-) diff --git a/include/boo/inputdev/DualshockPad.hpp b/include/boo/inputdev/DualshockPad.hpp index 50c4d85..c68932d 100644 --- a/include/boo/inputdev/DualshockPad.hpp +++ b/include/boo/inputdev/DualshockPad.hpp @@ -19,6 +19,7 @@ struct DualshockLED struct DualshockRumble { + uint8_t padding; uint8_t rightDuration; bool rightOn; uint8_t leftDuration; @@ -38,7 +39,7 @@ union DualshockOutReport DualshockLED led[4]; DualshockLED reserved; }; - uint8_t buf[36]; + uint8_t buf[49]; }; enum class EDualshockPadButtons @@ -130,7 +131,6 @@ class DualshockPad final : public DeviceBase uint8_t m_rumbleIntensity[2]; EDualshockLED m_led; DualshockOutReport m_report; - uint8_t m_btAddress[6]; void deviceDisconnected(); void initialCycle(); void transferCycle(); @@ -157,9 +157,9 @@ public: } } - void stopRumble(EDualshockMotor motor) + void stopRumble(int motor) { - m_rumbleRequest &= ~motor; + m_rumbleRequest &= ~EDualshockMotor(motor); } EDualshockLED getLED() @@ -180,7 +180,7 @@ public: void setRawLED(int led) { m_report.leds = led; - sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x0201); + sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01); } size_t getInputBufferSize() const { return 49; } diff --git a/lib/inputdev/CFPointer.hpp b/lib/inputdev/CFPointer.hpp index ddfed49..903986a 100644 --- a/lib/inputdev/CFPointer.hpp +++ b/lib/inputdev/CFPointer.hpp @@ -53,6 +53,15 @@ public: } operator bool() const { return storage != nullptr; } + void reset() + { + if (storage) + { + CFRelease(storage); + storage = nullptr; + } + } + private: CFTypeRef storage; diff --git a/lib/inputdev/DualshockPad.cpp b/lib/inputdev/DualshockPad.cpp index ca55ff4..918808b 100644 --- a/lib/inputdev/DualshockPad.cpp +++ b/lib/inputdev/DualshockPad.cpp @@ -17,14 +17,15 @@ static inline uint16_t bswap16(uint16_t val) {return __builtin_byteswap(val);} namespace boo { -static const uint8_t defaultReport[35] = { - 0x01, 0xff, 0x00, 0xff, 0x00, - 0xff, 0x80, 0x00, 0x00, 0x00, - 0xff, 0x27, 0x10, 0x00, 0x32, - 0xff, 0x27, 0x10, 0x00, 0x32, - 0xff, 0x27, 0x10, 0x00, 0x32, - 0xff, 0x27, 0x10, 0x00, 0x32, - 0x00, 0x00, 0x00, 0x00, 0x00 +static const uint8_t defaultReport[49] = { + 0x01, + 0x01, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x80, 0x00, 0x00, 0x00, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00 }; DualshockPad::DualshockPad(DeviceToken* token) @@ -33,7 +34,7 @@ DualshockPad::DualshockPad(DeviceToken* token) m_rumbleRequest(EDualshockMotor::None), m_rumbleState(EDualshockMotor::None) { - memcpy(m_report.buf, defaultReport, 35); + memcpy(m_report.buf, defaultReport, 49); } DualshockPad::~DualshockPad() @@ -49,8 +50,9 @@ void DualshockPad::deviceDisconnected() void DualshockPad::initialCycle() { - uint8_t setupCommand[4] = {0x42, 0x0c, 0x00, 0x00}; //Tells controller to start sending changes on in pipe - if (!sendHIDReport(setupCommand, sizeof(setupCommand), HIDReportType::Feature, 0xF4)) +#if 0 + uint8_t setupCommand[5] = {0xF4, 0x42, 0x0c, 0x00, 0x00}; //Tells controller to start sending changes on in pipe + if (!sendHIDReport(setupCommand, 5, HIDReportType::Feature, 0xF4)) { deviceError("Unable to send complete packet! Request size %x\n", sizeof(setupCommand)); return; @@ -59,6 +61,7 @@ void DualshockPad::initialCycle() receiveHIDReport(btAddr, sizeof(btAddr), HIDReportType::Feature, 0xF5); for (int i = 0; i < 6; i++) m_btAddress[5 - i] = btAddr[i + 2]; // Copy into buffer reversed, so it is LSB first +#endif } void DualshockPad::transferCycle() @@ -67,7 +70,11 @@ void DualshockPad::transferCycle() void DualshockPad::finalCycle() { - + m_report.rumble.leftDuration = 0; + m_report.rumble.leftForce = 0; + m_report.rumble.rightDuration = 0; + m_report.rumble.rightOn = false; + sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01); } void DualshockPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) @@ -80,6 +87,14 @@ void DualshockPad::receivedHIDReport(const uint8_t* data, size_t length, HIDRepo state.m_accelerometer[i] = bswap16(state.m_accelerometer[i]); state.m_gyrometerZ = bswap16(state.m_gyrometerZ); + const double zeroG = 511.5; // 1.65/3.3*1023 (1,65V); + float accXval = -((double)state.m_accelerometer[0] - zeroG); + float accYval = -((double)state.m_accelerometer[1] - zeroG); + float accZval = -((double)state.m_accelerometer[2] - zeroG); + state.accPitch = (atan2(accYval, accZval) + M_PI) * RAD_TO_DEG; + state.accYaw = (atan2(accXval, accZval) + M_PI) * RAD_TO_DEG; + state.gyroZ = (state.m_gyrometerZ / 1023.f); + if (m_callback) m_callback->controllerUpdate(*this, state); @@ -98,8 +113,8 @@ void DualshockPad::receivedHIDReport(const uint8_t* data, size_t length, HIDRepo if ((m_rumbleRequest & EDualshockMotor::Right) != EDualshockMotor::None) { - m_report.rumble.rightDuration = m_rumbleDuration[0]; - m_report.rumble.rightOn = true; + m_report.rumble.rightDuration = m_rumbleDuration[1]; + m_report.rumble.rightOn = m_rumbleIntensity[1] > 0; } else { @@ -116,13 +131,6 @@ void DualshockPad::receivedHIDReport(const uint8_t* data, size_t length, HIDRepo if (state.m_reserved5[7] & 0x01) m_rumbleRequest &= ~EDualshockMotor::Left; m_rumbleState = m_rumbleRequest; - const double zeroG = 511.5; // 1.65/3.3*1023 (1,65V); - float accXval = -((double)state.m_accelerometer[0] - zeroG); - float accYval = -((double)state.m_accelerometer[1] - zeroG); - float accZval = -((double)state.m_accelerometer[2] - zeroG); - state.accPitch = (atan2(accYval, accZval) + M_PI) * RAD_TO_DEG; - state.accYaw = (atan2(accXval, accZval) + M_PI) * RAD_TO_DEG; - state.gyroZ = (state.m_gyrometerZ / 1023.f); } } diff --git a/lib/inputdev/HIDDeviceIOKit.cpp b/lib/inputdev/HIDDeviceIOKit.cpp index 86bf979..30b3521 100644 --- a/lib/inputdev/HIDDeviceIOKit.cpp +++ b/lib/inputdev/HIDDeviceIOKit.cpp @@ -20,8 +20,9 @@ class HIDDeviceIOKit : public IHIDDevice IUnknownPointer m_usbIntf; uint8_t m_usbIntfInPipe = 0; uint8_t m_usbIntfOutPipe = 0; - IUnknownPointer m_hidIntf; + CFPointer m_hidIntf; bool m_runningTransferLoop = false; + bool m_isBt = false; const std::string& m_devPath; std::mutex m_initMutex; @@ -53,10 +54,11 @@ class HIDDeviceIOKit : public IHIDDevice bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { - if (m_hidIntf) + /* HACK: A bug in IOBluetoothGamepadHIDDriver prevents raw output report transmission + * USB driver appears to work correctly */ + if (m_hidIntf && !m_isBt) { - IOReturn res = m_hidIntf->setReport(m_hidIntf.storage(), IOHIDReportType(tp), message, data, length, - 1000, nullptr, nullptr, 0); + IOReturn res = IOHIDDeviceSetReport(m_hidIntf.get(), IOHIDReportType(tp), message, data, length); return res == kIOReturnSuccess; } return false; @@ -67,8 +69,7 @@ class HIDDeviceIOKit : public IHIDDevice if (m_hidIntf) { CFIndex readSize = length; - IOReturn res = m_hidIntf->getReport(m_hidIntf.storage(), IOHIDReportType(tp), message, data, &readSize, - 1000, nullptr, nullptr, 0); + IOReturn res = IOHIDDeviceGetReport(m_hidIntf.get(), IOHIDReportType(tp), message, data, &readSize); if (res != kIOReturnSuccess) return 0; return readSize; @@ -231,7 +232,8 @@ class HIDDeviceIOKit : public IHIDDevice std::unique_lock lk(device->m_initMutex); /* Get the HID element's object (HID device interface) */ - IOObjectPointer interfaceEntry = IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.c_str()); + IOObjectPointer interfaceEntry = + IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.c_str()); if (!IOObjectConformsTo(interfaceEntry.get(), "IOHIDDevice")) { snprintf(errStr, 256, "Unable to find interface for %s@%s\n", @@ -243,29 +245,8 @@ class HIDDeviceIOKit : public IHIDDevice return; } - /* IOKit Plugin COM interface (WTF Apple???) */ - IOCFPluginPointer iodev; - SInt32 score; - IOReturn err; - err = IOCreatePlugInInterfaceForService(interfaceEntry.get(), - kIOHIDDeviceTypeID, - kIOCFPlugInInterfaceID, - &iodev, - &score); - if (err) - { - snprintf(errStr, 256, "Unable to open %s@%s\n", - device->m_token.getProductName().c_str(), device->m_devPath.c_str()); - device->m_devImp.deviceError(errStr); - lk.unlock(); - device->m_initCond.notify_one(); - return; - } - - /* HID interface function-pointer table */ - IUnknownPointer intf; - err = iodev.As(&intf, kIOHIDDeviceDeviceInterfaceID); - if (err) + device->m_hidIntf = IOHIDDeviceCreate(nullptr, interfaceEntry.get()); + if (!device->m_hidIntf) { snprintf(errStr, 256, "Unable to open %s@%s\n", device->m_token.getProductName().c_str(), device->m_devPath.c_str()); @@ -276,8 +257,7 @@ class HIDDeviceIOKit : public IHIDDevice } /* Open device */ - device->m_hidIntf = intf; - err = intf->open(intf.storage(), kIOHIDOptionsTypeNone); + IOReturn err = IOHIDDeviceOpen(device->m_hidIntf.get(), kIOHIDOptionsTypeNone); if (err != kIOReturnSuccess) { if (err == kIOReturnExclusiveAccess) @@ -297,18 +277,18 @@ class HIDDeviceIOKit : public IHIDDevice return; } + /* Make note if device uses bluetooth driver */ + if (CFTypeRef transport = IOHIDDeviceGetProperty(device->m_hidIntf.get(), CFSTR(kIOHIDTransportKey))) + device->m_isBt = CFStringCompare(CFStringRef(transport), CFSTR(kIOHIDTransportBluetoothValue), 0) == kCFCompareEqualTo; + /* Register input buffer */ std::unique_ptr buffer; if (size_t bufSize = device->m_devImp.getInputBufferSize()) { buffer = std::unique_ptr(new uint8_t[bufSize]); - CFTypeRef source; - device->m_hidIntf->getAsyncEventSource(device->m_hidIntf.storage(), &source); - device->m_hidIntf->setInputReportCallback(device->m_hidIntf.storage(), buffer.get(), - bufSize, _hidReportCb, &device->m_devImp, 0); - CFRunLoopRef rl = CFRunLoopGetCurrent(); - CFRunLoopAddSource(rl, CFRunLoopSourceRef(source), kCFRunLoopDefaultMode); - CFRunLoopWakeUp(rl); + IOHIDDeviceRegisterInputReportCallback(device->m_hidIntf.get(), buffer.get(), bufSize, + _hidReportCb, &device->m_devImp); + IOHIDDeviceScheduleWithRunLoop(device->m_hidIntf.get(), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); } /* Return control to main thread */ @@ -320,14 +300,14 @@ class HIDDeviceIOKit : public IHIDDevice device->m_devImp.initialCycle(); while (device->m_runningTransferLoop) { - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.010, true); device->m_devImp.transferCycle(); } device->m_devImp.finalCycle(); /* Cleanup */ - intf->close(intf.storage(), kIOHIDOptionsTypeNone); - device->m_hidIntf = nullptr; + IOHIDDeviceClose(device->m_hidIntf.get(), kIOHIDOptionsTypeNone); + device->m_hidIntf.reset(); } void _deviceDisconnected() diff --git a/lib/inputdev/HIDDeviceUdev.cpp b/lib/inputdev/HIDDeviceUdev.cpp index 0be7643..67e5fb1 100644 --- a/lib/inputdev/HIDDeviceUdev.cpp +++ b/lib/inputdev/HIDDeviceUdev.cpp @@ -204,13 +204,20 @@ class HIDDeviceUdev final : public IHIDDevice device->m_devImp.initialCycle(); while (device->m_runningTransferLoop) { - while (true) + fd_set readset; + FD_ZERO(&readset); + FD_SET(fd, &readset); + struct timeval timeout = {0, 10000}; + if (select(fd + 1, &readset, nullptr, nullptr, &timeout) > 0) { - ssize_t sz = read(fd, readBuf.get(), readSz); - if (sz < 0) - break; - device->m_devImp.receivedHIDReport(readBuf.get(), sz, - HIDReportType::Input, readBuf[0]); + while (true) + { + ssize_t sz = read(fd, readBuf.get(), readSz); + if (sz < 0) + break; + device->m_devImp.receivedHIDReport(readBuf.get(), sz, + HIDReportType::Input, readBuf[0]); + } } device->m_devImp.transferCycle(); } @@ -245,23 +252,6 @@ class HIDDeviceUdev final : public IHIDDevice return false; return true; } - - /* - usbdevfs_ctrltransfer xfer = - { - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0x09, // HID_SET_REPORT - message, - 0, - (uint16_t)length, - 30, - (void*)data - }; - int ret = ioctl(m_devFd, USBDEVFS_CONTROL, &xfer); - if (ret != (int)length) - return false; - return true; - */ } return false; } @@ -278,20 +268,6 @@ class HIDDeviceUdev final : public IHIDDevice return 0; return length; } - - /* - usbdevfs_ctrltransfer xfer = - { - USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0x01, // HID_GET_REPORT - message, - 0, - (uint16_t)length, - 0, - (void*)data - }; - return ioctl(m_devFd, USBDEVFS_CONTROL, &xfer); - */ } return 0; } diff --git a/lib/inputdev/HIDDeviceWinUSB.cpp b/lib/inputdev/HIDDeviceWinUSB.cpp index f56b434..3a7f50f 100644 --- a/lib/inputdev/HIDDeviceWinUSB.cpp +++ b/lib/inputdev/HIDDeviceWinUSB.cpp @@ -174,6 +174,7 @@ class HIDDeviceWinUSB final : public IHIDDevice std::unique_lock lk(device->m_initMutex); /* POSIX.. who needs it?? -MS */ + m_overlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); device->m_hidHandle = CreateFileA(device->m_devPath.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, @@ -230,6 +231,7 @@ class HIDDeviceWinUSB final : public IHIDDevice device->m_devImp.finalCycle(); /* Cleanup */ + CloseHandle(m_overlapped.hEvent); CloseHandle(device->m_hidHandle); device->m_hidHandle = nullptr; } @@ -353,45 +355,37 @@ public: void ReadCycle(uint8_t* inBuffer, size_t inBufferSz) { - while (true) + ResetEvent(m_overlapped.hEvent); + ZeroMemory(inBuffer, inBufferSz); + DWORD BytesRead = 0; + BOOL Result = ReadFile(m_hidHandle, inBuffer, DWORD(inBufferSz), &BytesRead, &m_overlapped); + if (!Result) { - ZeroMemory(inBuffer, inBufferSz); - DWORD BytesRead = 0; - BOOL Result = ReadFile(m_hidHandle, inBuffer, DWORD(inBufferSz), &BytesRead, &m_overlapped); - if (!Result) + DWORD Error = GetLastError(); + if (Error != ERROR_IO_PENDING) { - DWORD Error = GetLastError(); - if (Error != ERROR_IO_PENDING) + fprintf(stderr, "Read Failed: %08X\n", Error); + return; + } + else + { + if (!GetOverlappedResult(m_hidHandle, &m_overlapped, &BytesRead, FALSE)) { + Error = GetLastError(); + if (Error == ERROR_IO_INCOMPLETE) + return; fprintf(stderr, "Read Failed: %08X\n", Error); return; } - else - { - if (!GetOverlappedResult(m_hidHandle, &m_overlapped, &BytesRead, TRUE)) - { - Error = GetLastError(); - if (Error == ERROR_IO_INCOMPLETE) - return; - fprintf(stderr, "Read Failed: %08X\n", Error); - return; - } - if (m_overlapped.Internal == STATUS_PENDING) - { - //std::cout << "Read Interrupted" << std::endl; - if(!CancelIo(m_hidHandle)) - { - Error = GetLastError(); - fprintf(stderr, "Cancel IO Failed: %08X\n", Error); - } - return; - } + if (WaitForSingleObject(m_overlapped.hEvent, 10) != WAIT_OBJECT_0) + { + return; } } - - m_devImp.receivedHIDReport(inBuffer, BytesRead, HIDReportType::Input, inBuffer[0]); } + + m_devImp.receivedHIDReport(inBuffer, BytesRead, HIDReportType::Input, inBuffer[0]); } };