Commit 9131f8cc authored by Roderick Colenbrander's avatar Roderick Colenbrander Committed by Jiri Kosina

HID: sony: Comply to Linux gamepad spec for DS4

The DS4 side of hid-sony used the hid-core layer to assign buttons
and axes based on the HID report descriptors. The default mapping
was strange e.g. right stick using ABS_Z/ABS_RZ or the physical
'south button' being reported as BTN_EAST etcetera.

This patch makes the DS4 side ofi the hid-sony driver comply to
the Linux game controller spec as suggested in a discussion with
Dmitry on the linux-input list.

Currently the main user of the DS4 is the SDL2 library, which has
a mapping table using vendor/device/version as a key. In order to
not break SDL2 we discussed adjusting the version number, so it
can have both mappings. This was discust on linux-input and we
discussed privately with SDL2 developers.
Signed-off-by: default avatarRoderick Colenbrander <roderick.colenbrander@sony.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent ac797b95
...@@ -977,6 +977,32 @@ static const unsigned int buzz_keymap[] = { ...@@ -977,6 +977,32 @@ static const unsigned int buzz_keymap[] = {
[20] = BTN_TRIGGER_HAPPY20, [20] = BTN_TRIGGER_HAPPY20,
}; };
static const unsigned int ds4_absmap[] = {
[0x30] = ABS_X,
[0x31] = ABS_Y,
[0x32] = ABS_RX, /* right stick X */
[0x33] = ABS_Z, /* L2 */
[0x34] = ABS_RZ, /* R2 */
[0x35] = ABS_RY, /* right stick Y */
};
static const unsigned int ds4_keymap[] = {
[0x1] = BTN_WEST, /* Square */
[0x2] = BTN_SOUTH, /* Cross */
[0x3] = BTN_EAST, /* Circle */
[0x4] = BTN_NORTH, /* Triangle */
[0x5] = BTN_TL, /* L1 */
[0x6] = BTN_TR, /* R1 */
[0x7] = BTN_TL2, /* L2 */
[0x8] = BTN_TR2, /* R2 */
[0x9] = BTN_SELECT, /* Share */
[0xa] = BTN_START, /* Options */
[0xb] = BTN_THUMBL, /* L3 */
[0xc] = BTN_THUMBR, /* R3 */
[0xd] = BTN_MODE, /* PS */
};
static enum power_supply_property sony_battery_props[] = { static enum power_supply_property sony_battery_props[] = {
POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY,
...@@ -1143,6 +1169,37 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi, ...@@ -1143,6 +1169,37 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi,
return 1; return 1;
} }
static int ds4_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
unsigned int key = usage->hid & HID_USAGE;
if (key >= ARRAY_SIZE(ds4_keymap))
return -1;
key = ds4_keymap[key];
hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key);
return 1;
} else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) {
unsigned int abs = usage->hid & HID_USAGE;
/* Let the HID parser deal with the HAT. */
if (usage->hid == HID_GD_HATSWITCH)
return 0;
if (abs >= ARRAY_SIZE(ds4_absmap))
return -1;
abs = ds4_absmap[abs];
hid_map_usage_clear(hi, usage, bit, max, EV_ABS, abs);
return 1;
}
return 0;
}
static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc, static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc,
unsigned int *rsize) unsigned int *rsize)
{ {
...@@ -1416,6 +1473,10 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, ...@@ -1416,6 +1473,10 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
if (sc->quirks & PS3REMOTE) if (sc->quirks & PS3REMOTE)
return ps3remote_mapping(hdev, hi, field, usage, bit, max); return ps3remote_mapping(hdev, hi, field, usage, bit, max);
if (sc->quirks & DUALSHOCK4_CONTROLLER)
return ds4_mapping(hdev, hi, field, usage, bit, max);
/* Let hid-core decide for the others */ /* Let hid-core decide for the others */
return 0; return 0;
} }
...@@ -2578,6 +2639,15 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -2578,6 +2639,15 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
else if (sc->quirks & SIXAXIS_CONTROLLER) else if (sc->quirks & SIXAXIS_CONTROLLER)
connect_mask |= HID_CONNECT_HIDDEV_FORCE; connect_mask |= HID_CONNECT_HIDDEV_FORCE;
/* Patch the hw version on DS4 compatible devices, so applications can
* distinguish between the default HID mappings and the mappings defined
* by the Linux game controller spec. This is important for the SDL2
* library, which has a game controller database, which uses device ids
* in combination with version as a key.
*/
if (sc->quirks & DUALSHOCK4_CONTROLLER)
hdev->version |= 0x8000;
ret = hid_hw_start(hdev, connect_mask); ret = hid_hw_start(hdev, connect_mask);
if (ret) { if (ret) {
hid_err(hdev, "hw start failed\n"); hid_err(hdev, "hw start failed\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment