Complete DualShock 3 support

This commit is contained in:
Lior Halphon 2020-05-03 20:23:37 +03:00
parent bb37f8d2f0
commit 6910c3d24b
2 changed files with 143 additions and 25 deletions

View File

@ -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,
},
}; };

View File

@ -24,6 +24,8 @@ static NSString const *JOYRumbleMin = @"JOYRumbleMin";
static NSString const *JOYRumbleMax = @"JOYRumbleMax"; static NSString const *JOYRumbleMax = @"JOYRumbleMax";
static NSString const *JOYSwapZRz = @"JOYSwapZRz"; static NSString const *JOYSwapZRz = @"JOYSwapZRz";
static NSString const *JOYActivationReport = @"JOYActivationReport"; static NSString const *JOYActivationReport = @"JOYActivationReport";
static NSString const *JOYIgnoredReports = @"JOYIgnoredReports";
static NSString const *JOYIsDualShock3 = @"JOYIsDualShock3";
static NSMutableDictionary<id, JOYController *> *controllers; // Physical controllers static NSMutableDictionary<id, JOYController *> *controllers; // Physical controllers
static NSMutableArray<JOYController *> *exposedControllers; // Logical controllers static NSMutableArray<JOYController *> *exposedControllers; // Logical controllers
@ -104,6 +106,30 @@ typedef struct __attribute__((packed)) {
uint8_t commandData[26]; uint8_t commandData[26];
} JOYSwitchPacket; } 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 @implementation JOYController
{ {
IOHIDDeviceRef _device; IOHIDDeviceRef _device;
@ -124,7 +150,8 @@ typedef struct __attribute__((packed)) {
NSMutableDictionary<NSValue *, JOYElement *> *_iokitToJOY; NSMutableDictionary<NSValue *, JOYElement *> *_iokitToJOY;
NSString *_serialSuffix; NSString *_serialSuffix;
bool _isSwitch; // Does this controller use the Switch protocol? bool _isSwitch; // Does this controller use the Switch protocol?
JOYSwitchPacket _lastSwitchPacket; bool _isDualShock3; // Does this controller use DS3 outputs?
JOYVendorSpecificOutput _lastVendorSpecificOutput;
NSLock *_rumblePWMThreadLock; NSLock *_rumblePWMThreadLock;
volatile double _rumblePWMRatio; volatile double _rumblePWMRatio;
bool _physicallyConnected; bool _physicallyConnected;
@ -335,6 +362,8 @@ typedef struct __attribute__((packed)) {
_hacks = hacks; _hacks = hacks;
_isSwitch = [_hacks[JOYIsSwitch] boolValue]; _isSwitch = [_hacks[JOYIsSwitch] boolValue];
_isDualShock3 = [_hacks[JOYIsDualShock3] boolValue];
NSDictionary *customReports = hacks[JOYCustomReports]; NSDictionary *customReports = hacks[JOYCustomReports];
if (hacks[JOYCustomReports]) { if (hacks[JOYCustomReports]) {
@ -384,6 +413,11 @@ typedef struct __attribute__((packed)) {
} }
id previous = nil; id previous = nil;
NSSet *ignoredReports = nil;
if (hacks[ignoredReports]) {
ignoredReports = [NSSet setWithArray:hacks[ignoredReports]];
}
for (id _element in array) { for (id _element in array) {
if (_element == previous) continue; // Some elements are reported twice for some reason if (_element == previous) continue; // Some elements are reported twice for some reason
previous = _element; previous = _element;
@ -409,8 +443,8 @@ typedef struct __attribute__((packed)) {
case kIOHIDElementTypeCollection: case kIOHIDElementTypeCollection:
continue; continue;
} }
if ((!isOutput && customReports[@(element.reportID)]) || if ((!isOutput && [ignoredReports containsObject:@(element.reportID)]) ||
(isOutput && customReports[@(-element.reportID)])) continue; (isOutput && [ignoredReports containsObject:@(-element.reportID)])) continue;
if (IOHIDElementIsArray((__bridge IOHIDElementRef)_element)) continue; if (IOHIDElementIsArray((__bridge IOHIDElementRef)_element)) continue;
@ -423,13 +457,6 @@ typedef struct __attribute__((packed)) {
} }
_iokitToJOY[@(IOHIDElementGetCookie((__bridge IOHIDElementRef)_element))] = element; _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]; [exposedControllers addObject:self];
@ -450,6 +477,20 @@ typedef struct __attribute__((packed)) {
[self sendReport:[NSData dataWithBytes:(uint8_t[]){0x80, 0x02} length:2]]; [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; return self;
} }
@ -653,7 +694,7 @@ typedef struct __attribute__((packed)) {
} }
} }
_physicallyConnected = false; _physicallyConnected = false;
[self setRumbleAmplitude:0]; // Stop the rumble thread. [self _forceStopPWMThread]; // Stop the rumble thread.
[exposedControllers removeObject:self]; [exposedControllers removeObject:self];
_device = nil; _device = nil;
} }
@ -669,13 +710,18 @@ typedef struct __attribute__((packed)) {
{ {
mask &= 0xF; mask &= 0xF;
if (_isSwitch) { if (_isSwitch) {
_lastSwitchPacket.reportID = 0x1; // Rumble and LEDs _lastVendorSpecificOutput.switchPacket.reportID = 0x1; // Rumble and LEDs
_lastSwitchPacket.sequence++; _lastVendorSpecificOutput.switchPacket.sequence++;
_lastSwitchPacket.sequence &= 0xF; _lastVendorSpecificOutput.switchPacket.sequence &= 0xF;
_lastSwitchPacket.command = 0x30; // LED _lastVendorSpecificOutput.switchPacket.command = 0x30; // LED
_lastSwitchPacket.commandData[0] = mask; _lastVendorSpecificOutput.switchPacket.commandData[0] = mask;
//[self sendReport:[NSData dataWithBytes:&_lastSwitchPacket length:sizeof(_lastSwitchPacket)]]; //[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 - (void)pwmThread
@ -727,17 +773,23 @@ typedef struct __attribute__((packed)) {
lowAmp = 0; lowAmp = 0;
} }
_lastSwitchPacket.rumbleData[0] = _lastSwitchPacket.rumbleData[4] = highFreq & 0xFF; _lastVendorSpecificOutput.switchPacket.rumbleData[0] = _lastVendorSpecificOutput.switchPacket.rumbleData[4] = highFreq & 0xFF;
_lastSwitchPacket.rumbleData[1] = _lastSwitchPacket.rumbleData[5] = (highAmp << 1) + ((highFreq >> 8) & 0x1); _lastVendorSpecificOutput.switchPacket.rumbleData[1] = _lastVendorSpecificOutput.switchPacket.rumbleData[5] = (highAmp << 1) + ((highFreq >> 8) & 0x1);
_lastSwitchPacket.rumbleData[2] = _lastSwitchPacket.rumbleData[6] = lowFreq; _lastVendorSpecificOutput.switchPacket.rumbleData[2] = _lastVendorSpecificOutput.switchPacket.rumbleData[6] = lowFreq;
_lastSwitchPacket.rumbleData[3] = _lastSwitchPacket.rumbleData[7] = lowAmp; _lastVendorSpecificOutput.switchPacket.rumbleData[3] = _lastVendorSpecificOutput.switchPacket.rumbleData[7] = lowAmp;
_lastSwitchPacket.reportID = 0x10; // Rumble only _lastVendorSpecificOutput.switchPacket.reportID = 0x10; // Rumble only
_lastSwitchPacket.sequence++; _lastVendorSpecificOutput.switchPacket.sequence++;
_lastSwitchPacket.sequence &= 0xF; _lastVendorSpecificOutput.switchPacket.sequence &= 0xF;
_lastSwitchPacket.command = 0; // LED _lastVendorSpecificOutput.switchPacket.command = 0; // LED
[self sendReport:[NSData dataWithBytes:&_lastSwitchPacket length:sizeof(_lastSwitchPacket)]]; [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 { else {
if (_rumbleElement.max == 1 && _rumbleElement.min == 0) { if (_rumbleElement.max == 1 && _rumbleElement.min == 0) {