Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 510c8b7c authored by Roderick Colenbrander's avatar Roderick Colenbrander Committed by Jiri Kosina
Browse files

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: default avatarRoderick Colenbrander <roderick.colenbrander@sony.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 80ecc48c
Loading
Loading
Loading
Loading
+105 −25
Original line number Diff line number Diff line
@@ -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,15 +1305,29 @@ 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;

	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);
@@ -1312,6 +1346,8 @@ static int sony_register_sensors(struct sony_sc *sc)

		__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);