Complete DualShock 3 support
This commit is contained in:
parent
bb37f8d2f0
commit
6910c3d24b
@ -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,
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user