mirror of
https://github.com/AxioDL/boo.git
synced 2025-06-04 13:41:32 +00:00
Working Dualshock output reports on macOS; fix runaway loop
This commit is contained in:
parent
7823aecc57
commit
6e1c71aefd
@ -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; }
|
||||
|
@ -53,6 +53,15 @@ public:
|
||||
}
|
||||
operator bool() const { return storage != nullptr; }
|
||||
|
||||
void reset()
|
||||
{
|
||||
if (storage)
|
||||
{
|
||||
CFRelease(storage);
|
||||
storage = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CFTypeRef storage;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,9 @@ class HIDDeviceIOKit : public IHIDDevice
|
||||
IUnknownPointer<IOUSBInterfaceInterface> m_usbIntf;
|
||||
uint8_t m_usbIntfInPipe = 0;
|
||||
uint8_t m_usbIntfOutPipe = 0;
|
||||
IUnknownPointer<IOHIDDeviceDeviceInterface> m_hidIntf;
|
||||
CFPointer<IOHIDDeviceRef> 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<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
/* Get the HID element's object (HID device interface) */
|
||||
IOObjectPointer<io_service_t> interfaceEntry = IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.c_str());
|
||||
IOObjectPointer<io_service_t> 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<IOHIDDeviceDeviceInterface> 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<uint8_t[]> buffer;
|
||||
if (size_t bufSize = device->m_devImp.getInputBufferSize())
|
||||
{
|
||||
buffer = std::unique_ptr<uint8_t[]>(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()
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -174,6 +174,7 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
||||
std::unique_lock<std::mutex> 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]);
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user