From 6910c3d24bb973a18973fc496c564df86293dbbc Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 3 May 2020 20:23:37 +0300 Subject: [PATCH] Complete DualShock 3 support --- JoyKit/ControllerConfiguration.inc | 66 +++++++++++++++++++ JoyKit/JOYController.m | 102 ++++++++++++++++++++++------- 2 files changed, 143 insertions(+), 25 deletions(-) diff --git a/JoyKit/ControllerConfiguration.inc b/JoyKit/ControllerConfiguration.inc index b7dbfe8..ea3ba9a 100644 --- a/JoyKit/ControllerConfiguration.inc +++ b/JoyKit/ControllerConfiguration.inc @@ -408,4 +408,70 @@ hacksByName = @{ ], }, }, + + JOYIgnoredReports: @(0x30), // Ignore the real 0x30 report as it's broken + + @"PLAYSTATION(R)3 Controller": @{ // DualShock 3 + JOYAxisGroups: @{ + @(kHIDUsage_GD_X): @(0), + @(kHIDUsage_GD_Y): @(0), + @(kHIDUsage_GD_Z): @(1), + @(kHIDUsage_GD_Rx): @(2), + @(kHIDUsage_GD_Ry): @(3), + @(kHIDUsage_GD_Rz): @(1), + }, + + JOYButtonUsageMapping: @{ + BUTTON(1): @(JOYButtonUsageSelect), + BUTTON(2): @(JOYButtonUsageL3), + BUTTON(3): @(JOYButtonUsageR3), + BUTTON(4): @(JOYButtonUsageStart), + BUTTON(5): @(JOYButtonUsageDPadUp), + BUTTON(6): @(JOYButtonUsageDPadRight), + BUTTON(7): @(JOYButtonUsageDPadDown), + BUTTON(8): @(JOYButtonUsageDPadLeft), + BUTTON(9): @(JOYButtonUsageL2), + BUTTON(10): @(JOYButtonUsageR2), + BUTTON(11): @(JOYButtonUsageL1), + BUTTON(12): @(JOYButtonUsageR1), + BUTTON(13): @(JOYButtonUsageX), + BUTTON(14): @(JOYButtonUsageA), + BUTTON(15): @(JOYButtonUsageB), + BUTTON(16): @(JOYButtonUsageY), + BUTTON(17): @(JOYButtonUsageHome), + }, + + JOYAxisUsageMapping: @{ + AXIS(4): @(JOYAxisUsageL1), + AXIS(5): @(JOYAxisUsageR1), + AXIS(8): @(JOYAxisUsageL2), + AXIS(9): @(JOYAxisUsageR2), + }, + + JOYAxes2DUsageMapping: @{ + AXES2D(1): @(JOYAxes2DUsageLeftStick), + AXES2D(3): @(JOYAxes2DUsageRightStick), + }, + + JOYCustomReports: @{ + @(0x01): @[ + /* Pressure sensitive inputs */ + @{@"reportID": @(1), @"size":@8, @"offset":@(13 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255}, + @{@"reportID": @(1), @"size":@8, @"offset":@(14 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255}, + @{@"reportID": @(1), @"size":@8, @"offset":@(15 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255}, + @{@"reportID": @(1), @"size":@8, @"offset":@(16 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255}, + @{@"reportID": @(1), @"size":@8, @"offset":@(17 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Dial), @"min": @0, @"max": @255}, + @{@"reportID": @(1), @"size":@8, @"offset":@(18 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Wheel), @"min": @0, @"max": @255}, + @{@"reportID": @(1), @"size":@8, @"offset":@(19 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Rx), @"min": @0, @"max": @255}, + @{@"reportID": @(1), @"size":@8, @"offset":@(20 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Ry), @"min": @0, @"max": @255}, + @{@"reportID": @(1), @"size":@8, @"offset":@(21 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255}, + @{@"reportID": @(1), @"size":@8, @"offset":@(22 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255}, + @{@"reportID": @(1), @"size":@8, @"offset":@(23 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255}, + @{@"reportID": @(1), @"size":@8, @"offset":@(24 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255}, + ] + }, + + JOYIsDualShock3: @YES, + }, + }; diff --git a/JoyKit/JOYController.m b/JoyKit/JOYController.m index beb61a3..015737e 100644 --- a/JoyKit/JOYController.m +++ b/JoyKit/JOYController.m @@ -24,6 +24,8 @@ static NSString const *JOYRumbleMin = @"JOYRumbleMin"; static NSString const *JOYRumbleMax = @"JOYRumbleMax"; static NSString const *JOYSwapZRz = @"JOYSwapZRz"; static NSString const *JOYActivationReport = @"JOYActivationReport"; +static NSString const *JOYIgnoredReports = @"JOYIgnoredReports"; +static NSString const *JOYIsDualShock3 = @"JOYIsDualShock3"; static NSMutableDictionary *controllers; // Physical controllers static NSMutableArray *exposedControllers; // Logical controllers @@ -104,6 +106,30 @@ typedef struct __attribute__((packed)) { uint8_t commandData[26]; } JOYSwitchPacket; +typedef struct __attribute__((packed)) { + uint8_t reportID; + uint8_t padding; + uint8_t rumbleRightDuration; + uint8_t rumbleRightStrength; + uint8_t rumbleLeftDuration; + uint8_t rumbleLeftStrength; + uint32_t padding2; + uint8_t ledsEnabled; + struct { + uint8_t timeEnabled; + uint8_t dutyLength; + uint8_t enabled; + uint8_t dutyOff; + uint8_t dutyOnn; + } __attribute__((packed)) led[5]; + uint8_t padding3[13]; +} JOYDualShock3Output; + +typedef union { + JOYSwitchPacket switchPacket; + JOYDualShock3Output ds3Output; +} JOYVendorSpecificOutput; + @implementation JOYController { IOHIDDeviceRef _device; @@ -124,7 +150,8 @@ typedef struct __attribute__((packed)) { NSMutableDictionary *_iokitToJOY; NSString *_serialSuffix; bool _isSwitch; // Does this controller use the Switch protocol? - JOYSwitchPacket _lastSwitchPacket; + bool _isDualShock3; // Does this controller use DS3 outputs? + JOYVendorSpecificOutput _lastVendorSpecificOutput; NSLock *_rumblePWMThreadLock; volatile double _rumblePWMRatio; bool _physicallyConnected; @@ -335,6 +362,8 @@ typedef struct __attribute__((packed)) { _hacks = hacks; _isSwitch = [_hacks[JOYIsSwitch] boolValue]; + _isDualShock3 = [_hacks[JOYIsDualShock3] boolValue]; + NSDictionary *customReports = hacks[JOYCustomReports]; if (hacks[JOYCustomReports]) { @@ -384,6 +413,11 @@ typedef struct __attribute__((packed)) { } id previous = nil; + NSSet *ignoredReports = nil; + if (hacks[ignoredReports]) { + ignoredReports = [NSSet setWithArray:hacks[ignoredReports]]; + } + for (id _element in array) { if (_element == previous) continue; // Some elements are reported twice for some reason previous = _element; @@ -409,8 +443,8 @@ typedef struct __attribute__((packed)) { case kIOHIDElementTypeCollection: continue; } - if ((!isOutput && customReports[@(element.reportID)]) || - (isOutput && customReports[@(-element.reportID)])) continue; + if ((!isOutput && [ignoredReports containsObject:@(element.reportID)]) || + (isOutput && [ignoredReports containsObject:@(-element.reportID)])) continue; if (IOHIDElementIsArray((__bridge IOHIDElementRef)_element)) continue; @@ -423,13 +457,6 @@ typedef struct __attribute__((packed)) { } _iokitToJOY[@(IOHIDElementGetCookie((__bridge IOHIDElementRef)_element))] = element; - - if (_isSwitch && element.reportID == 0x30) { - /* This report does not match its report descriptor (The descriptor ignores several fields) so - we can't use the elements in it directly.*/ - continue; - } - } [exposedControllers addObject:self]; @@ -450,6 +477,20 @@ typedef struct __attribute__((packed)) { [self sendReport:[NSData dataWithBytes:(uint8_t[]){0x80, 0x02} length:2]]; } + if (_isDualShock3) { + _lastVendorSpecificOutput.ds3Output = (JOYDualShock3Output){ + .reportID = 1, + .led = { + {.timeEnabled = 0xff, .dutyLength = 0x27, .enabled = 0x10, .dutyOff = 0, .dutyOnn = 0x32}, + {.timeEnabled = 0xff, .dutyLength = 0x27, .enabled = 0x10, .dutyOff = 0, .dutyOnn = 0x32}, + {.timeEnabled = 0xff, .dutyLength = 0x27, .enabled = 0x10, .dutyOff = 0, .dutyOnn = 0x32}, + {.timeEnabled = 0xff, .dutyLength = 0x27, .enabled = 0x10, .dutyOff = 0, .dutyOnn = 0x32}, + {.timeEnabled = 0, .dutyLength = 0, .enabled = 0, .dutyOff = 0, .dutyOnn = 0}, + } + }; + + } + return self; } @@ -653,7 +694,7 @@ typedef struct __attribute__((packed)) { } } _physicallyConnected = false; - [self setRumbleAmplitude:0]; // Stop the rumble thread. + [self _forceStopPWMThread]; // Stop the rumble thread. [exposedControllers removeObject:self]; _device = nil; } @@ -669,13 +710,18 @@ typedef struct __attribute__((packed)) { { mask &= 0xF; if (_isSwitch) { - _lastSwitchPacket.reportID = 0x1; // Rumble and LEDs - _lastSwitchPacket.sequence++; - _lastSwitchPacket.sequence &= 0xF; - _lastSwitchPacket.command = 0x30; // LED - _lastSwitchPacket.commandData[0] = mask; + _lastVendorSpecificOutput.switchPacket.reportID = 0x1; // Rumble and LEDs + _lastVendorSpecificOutput.switchPacket.sequence++; + _lastVendorSpecificOutput.switchPacket.sequence &= 0xF; + _lastVendorSpecificOutput.switchPacket.command = 0x30; // LED + _lastVendorSpecificOutput.switchPacket.commandData[0] = mask; //[self sendReport:[NSData dataWithBytes:&_lastSwitchPacket length:sizeof(_lastSwitchPacket)]]; } + else if (_isDualShock3) { + _lastVendorSpecificOutput.ds3Output.reportID = 1; + _lastVendorSpecificOutput.ds3Output.ledsEnabled = mask << 1; + [self sendReport:[NSData dataWithBytes:&_lastVendorSpecificOutput.ds3Output length:sizeof(_lastVendorSpecificOutput.ds3Output)]]; + } } - (void)pwmThread @@ -727,17 +773,23 @@ typedef struct __attribute__((packed)) { lowAmp = 0; } - _lastSwitchPacket.rumbleData[0] = _lastSwitchPacket.rumbleData[4] = highFreq & 0xFF; - _lastSwitchPacket.rumbleData[1] = _lastSwitchPacket.rumbleData[5] = (highAmp << 1) + ((highFreq >> 8) & 0x1); - _lastSwitchPacket.rumbleData[2] = _lastSwitchPacket.rumbleData[6] = lowFreq; - _lastSwitchPacket.rumbleData[3] = _lastSwitchPacket.rumbleData[7] = lowAmp; + _lastVendorSpecificOutput.switchPacket.rumbleData[0] = _lastVendorSpecificOutput.switchPacket.rumbleData[4] = highFreq & 0xFF; + _lastVendorSpecificOutput.switchPacket.rumbleData[1] = _lastVendorSpecificOutput.switchPacket.rumbleData[5] = (highAmp << 1) + ((highFreq >> 8) & 0x1); + _lastVendorSpecificOutput.switchPacket.rumbleData[2] = _lastVendorSpecificOutput.switchPacket.rumbleData[6] = lowFreq; + _lastVendorSpecificOutput.switchPacket.rumbleData[3] = _lastVendorSpecificOutput.switchPacket.rumbleData[7] = lowAmp; - _lastSwitchPacket.reportID = 0x10; // Rumble only - _lastSwitchPacket.sequence++; - _lastSwitchPacket.sequence &= 0xF; - _lastSwitchPacket.command = 0; // LED - [self sendReport:[NSData dataWithBytes:&_lastSwitchPacket length:sizeof(_lastSwitchPacket)]]; + _lastVendorSpecificOutput.switchPacket.reportID = 0x10; // Rumble only + _lastVendorSpecificOutput.switchPacket.sequence++; + _lastVendorSpecificOutput.switchPacket.sequence &= 0xF; + _lastVendorSpecificOutput.switchPacket.command = 0; // LED + [self sendReport:[NSData dataWithBytes:&_lastVendorSpecificOutput.switchPacket length:sizeof(_lastVendorSpecificOutput.switchPacket)]]; + } + else if (_isDualShock3) { + _lastVendorSpecificOutput.ds3Output.reportID = 1; + _lastVendorSpecificOutput.ds3Output.rumbleLeftDuration = _lastVendorSpecificOutput.ds3Output.rumbleRightDuration = amp? 0xff : 0; + _lastVendorSpecificOutput.ds3Output.rumbleLeftStrength = _lastVendorSpecificOutput.ds3Output.rumbleRightStrength = amp * 0xff; + [self sendReport:[NSData dataWithBytes:&_lastVendorSpecificOutput.ds3Output length:sizeof(_lastVendorSpecificOutput.ds3Output)]]; } else { if (_rumbleElement.max == 1 && _rumbleElement.min == 0) {