HID: sony: Expose DS3 motion sensors through separate device

This patch adds a separate evdev node for the DS3 its motion
sensors. We only expose the accelerometers as the gyroscope
is extremely difficult to manage and behavior varies a lot
between hardware revisions.

Signed-off-by: Roderick Colenbrander <roderick.colenbrander@sony.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
Roderick Colenbrander 2017-03-07 15:45:10 -08:00 committed by Jiri Kosina
parent 80ecc48c0a
commit 510c8b7c16

View File

@ -547,12 +547,15 @@ struct motion_output_report_02 {
#define DS4_INPUT_REPORT_BATTERY_OFFSET 30
#define DS4_INPUT_REPORT_TOUCHPAD_OFFSET 33
#define DS4_SENSOR_SUFFIX " Motion Sensors"
#define SENSOR_SUFFIX " Motion Sensors"
#define DS4_TOUCHPAD_SUFFIX " Touchpad"
#define DS4_GYRO_RES_PER_DEG_S 1024
#define DS4_ACC_RES_PER_G 8192
#define SIXAXIS_INPUT_REPORT_ACC_X_OFFSET 41
#define SIXAXIS_ACC_RES_PER_G 113
static DEFINE_SPINLOCK(sony_dev_list_lock);
static LIST_HEAD(sony_device_list);
static DEFINE_IDA(sony_device_id_allocator);
@ -841,6 +844,23 @@ static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size)
sc->battery_capacity = battery_capacity;
sc->battery_charging = battery_charging;
spin_unlock_irqrestore(&sc->lock, flags);
if (sc->quirks & SIXAXIS_CONTROLLER) {
int val;
offset = SIXAXIS_INPUT_REPORT_ACC_X_OFFSET;
val = ((rd[offset+1] << 8) | rd[offset]) - 511;
input_report_abs(sc->sensor_dev, ABS_X, val);
/* Y and Z are swapped and inversed */
val = 511 - ((rd[offset+5] << 8) | rd[offset+4]);
input_report_abs(sc->sensor_dev, ABS_Y, val);
val = 511 - ((rd[offset+3] << 8) | rd[offset+2]);
input_report_abs(sc->sensor_dev, ABS_Z, val);
input_sync(sc->sensor_dev);
}
}
static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
@ -1285,33 +1305,49 @@ static int sony_register_sensors(struct sony_sc *sc)
/* Append a suffix to the controller name as there are various
* DS4 compatible non-Sony devices with different names.
*/
name_sz = strlen(sc->hdev->name) + sizeof(DS4_SENSOR_SUFFIX);
name_sz = strlen(sc->hdev->name) + sizeof(SENSOR_SUFFIX);
name = kzalloc(name_sz, GFP_KERNEL);
if (!name) {
ret = -ENOMEM;
goto err;
}
snprintf(name, name_sz, "%s" DS4_SENSOR_SUFFIX, sc->hdev->name);
snprintf(name, name_sz, "%s" SENSOR_SUFFIX, sc->hdev->name);
sc->sensor_dev->name = name;
range = DS4_ACC_RES_PER_G*4;
input_set_abs_params(sc->sensor_dev, ABS_X, -range, range, 16, 0);
input_set_abs_params(sc->sensor_dev, ABS_Y, -range, range, 16, 0);
input_set_abs_params(sc->sensor_dev, ABS_Z, -range, range, 16, 0);
input_abs_set_res(sc->sensor_dev, ABS_X, DS4_ACC_RES_PER_G);
input_abs_set_res(sc->sensor_dev, ABS_Y, DS4_ACC_RES_PER_G);
input_abs_set_res(sc->sensor_dev, ABS_Z, DS4_ACC_RES_PER_G);
if (sc->quirks & SIXAXIS_CONTROLLER) {
/* For the DS3 we only support the accelerometer, which works
* quite well even without calibration. The device also has
* a 1-axis gyro, but it is very difficult to manage from within
* the driver even to get data, the sensor is inaccurate and
* the behavior is very different between hardware revisions.
*/
input_set_abs_params(sc->sensor_dev, ABS_X, -512, 511, 4, 0);
input_set_abs_params(sc->sensor_dev, ABS_Y, -512, 511, 4, 0);
input_set_abs_params(sc->sensor_dev, ABS_Z, -512, 511, 4, 0);
input_abs_set_res(sc->sensor_dev, ABS_X, SIXAXIS_ACC_RES_PER_G);
input_abs_set_res(sc->sensor_dev, ABS_Y, SIXAXIS_ACC_RES_PER_G);
input_abs_set_res(sc->sensor_dev, ABS_Z, SIXAXIS_ACC_RES_PER_G);
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
range = DS4_ACC_RES_PER_G*4;
input_set_abs_params(sc->sensor_dev, ABS_X, -range, range, 16, 0);
input_set_abs_params(sc->sensor_dev, ABS_Y, -range, range, 16, 0);
input_set_abs_params(sc->sensor_dev, ABS_Z, -range, range, 16, 0);
input_abs_set_res(sc->sensor_dev, ABS_X, DS4_ACC_RES_PER_G);
input_abs_set_res(sc->sensor_dev, ABS_Y, DS4_ACC_RES_PER_G);
input_abs_set_res(sc->sensor_dev, ABS_Z, DS4_ACC_RES_PER_G);
range = DS4_GYRO_RES_PER_DEG_S*2048;
input_set_abs_params(sc->sensor_dev, ABS_RX, -range, range, 16, 0);
input_set_abs_params(sc->sensor_dev, ABS_RY, -range, range, 16, 0);
input_set_abs_params(sc->sensor_dev, ABS_RZ, -range, range, 16, 0);
input_abs_set_res(sc->sensor_dev, ABS_RX, DS4_GYRO_RES_PER_DEG_S);
input_abs_set_res(sc->sensor_dev, ABS_RY, DS4_GYRO_RES_PER_DEG_S);
input_abs_set_res(sc->sensor_dev, ABS_RZ, DS4_GYRO_RES_PER_DEG_S);
range = DS4_GYRO_RES_PER_DEG_S*2048;
input_set_abs_params(sc->sensor_dev, ABS_RX, -range, range, 16, 0);
input_set_abs_params(sc->sensor_dev, ABS_RY, -range, range, 16, 0);
input_set_abs_params(sc->sensor_dev, ABS_RZ, -range, range, 16, 0);
input_abs_set_res(sc->sensor_dev, ABS_RX, DS4_GYRO_RES_PER_DEG_S);
input_abs_set_res(sc->sensor_dev, ABS_RY, DS4_GYRO_RES_PER_DEG_S);
input_abs_set_res(sc->sensor_dev, ABS_RZ, DS4_GYRO_RES_PER_DEG_S);
__set_bit(EV_MSC, sc->sensor_dev->evbit);
__set_bit(MSC_TIMESTAMP, sc->sensor_dev->mscbit);
}
__set_bit(EV_MSC, sc->sensor_dev->evbit);
__set_bit(MSC_TIMESTAMP, sc->sensor_dev->mscbit);
__set_bit(INPUT_PROP_ACCELEROMETER, sc->sensor_dev->propbit);
ret = input_register_device(sc->sensor_dev);
@ -2447,8 +2483,7 @@ static int sony_input_configured(struct hid_device *hdev,
goto err_stop;
}
if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
(sc->quirks & NAVIGATION_CONTROLLER_USB)) {
if (sc->quirks & NAVIGATION_CONTROLLER_USB) {
/*
* The Sony Sixaxis does not handle HID Output Reports on the
* Interrupt EP like it could, so we need to force HID Output
@ -2476,8 +2511,46 @@ static int sony_input_configured(struct hid_device *hdev,
}
sony_init_output_report(sc, sixaxis_send_output_report);
} else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) ||
(sc->quirks & NAVIGATION_CONTROLLER_BT)) {
} else if (sc->quirks & NAVIGATION_CONTROLLER_BT) {
/*
* The Navigation controller wants output reports sent on the ctrl
* endpoint when connected via Bluetooth.
*/
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
ret = sixaxis_set_operational_bt(hdev);
if (ret < 0) {
hid_err(hdev, "Failed to set controller into operational mode\n");
goto err_stop;
}
sony_init_output_report(sc, sixaxis_send_output_report);
} else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
/*
* The Sony Sixaxis does not handle HID Output Reports on the
* Interrupt EP and the device only becomes active when the
* PS button is pressed. See comment for Navigation controller
* above for more details.
*/
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
sc->defer_initialization = 1;
ret = sixaxis_set_operational_usb(hdev);
if (ret < 0) {
hid_err(hdev, "Failed to set controller into operational mode\n");
goto err_stop;
}
ret = sony_register_sensors(sc);
if (ret) {
hid_err(sc->hdev,
"Unable to initialize motion sensors: %d\n", ret);
goto err_stop;
}
sony_init_output_report(sc, sixaxis_send_output_report);
} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
/*
* The Sixaxis wants output reports sent on the ctrl endpoint
* when connected via Bluetooth.
@ -2490,6 +2563,13 @@ static int sony_input_configured(struct hid_device *hdev,
goto err_stop;
}
ret = sony_register_sensors(sc);
if (ret) {
hid_err(sc->hdev,
"Unable to initialize motion sensors: %d\n", ret);
goto err_stop;
}
sony_init_output_report(sc, sixaxis_send_output_report);
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
ret = dualshock4_get_calibration_data(sc);