[PATCH] Support for Sony NSG-MR5U

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,
This is a patch for Sony's GoogleTV multitouch bluetooth remotes, it adds into 
the shared hid-sony module, and is based on the methods used in the apple 
magicmouse module.  It builds against the 3.9.0 git tree at:
git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git
Please let me know how it looks, this is my first patch.
Thank you
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 512b01c..a61d9dc 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1701,6 +1701,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY_TOUCH_REMOTE, USB_DEVICE_ID_SONY_TOUCH_REMOTE_LYRA) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY_TOUCH_REMOTE, USB_DEVICE_ID_SONY_TOUCH_REMOTE_LEO) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 92e47e5..fd85e26 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -723,6 +723,10 @@
 #define USB_DEVICE_ID_SONY_PS3_CONTROLLER	0x0268
 #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER	0x042f
 
+#define USB_VENDOR_ID_SONY_TOUCH_REMOTE		0x0609
+#define USB_DEVICE_ID_SONY_TOUCH_REMOTE_LYRA	0x0368
+#define USB_DEVICE_ID_SONY_TOUCH_REMOTE_LEO	0x0369
+
 #define USB_VENDOR_ID_SOUNDGRAPH	0x15c2
 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST	0x0034
 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST	0x0046
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 7a1ebb8..53b3eb9 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -1180,6 +1180,14 @@ static const struct hid_device_id mt_devices[] = {
 		MT_USB_DEVICE(USB_VENDOR_ID_QUANTA,
 			USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008) },
 
+	/* Sony CE Remote */
+	{ .driver_data = MT_CLS_DEFAULT,
+		MT_BT_DEVICE(USB_VENDOR_ID_SONY_TOUCH_REMOTE,
+			USB_DEVICE_ID_SONY_TOUCH_REMOTE_LYRA) },
+	{ .driver_data = MT_CLS_DEFAULT,
+		MT_BT_DEVICE(USB_VENDOR_ID_SONY_TOUCH_REMOTE,
+			USB_DEVICE_ID_SONY_TOUCH_REMOTE_LEO) },
+
 	/* Stantum panels */
 	{ .driver_data = MT_CLS_CONFIDENCE,
 		MT_USB_DEVICE(USB_VENDOR_ID_STANTUM,
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 312098e..44e81f8 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -6,6 +6,7 @@
  *  Copyright (c) 2005 Michael Haboustak <mike-@xxxxxxxxxxxx> for Concept2, Inc
  *  Copyright (c) 2008 Jiri Slaby
  *  Copyright (c) 2006-2008 Jiri Kosina
+ *  Copyright (c) 2013 Jason Flatt <jflatt@xxxxxxx>
  */
 
 /*
@@ -17,6 +18,7 @@
 
 #include <linux/device.h>
 #include <linux/hid.h>
+#include <linux/input/mt.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/usb.h>
@@ -26,6 +28,17 @@
 #define VAIO_RDESC_CONSTANT     (1 << 0)
 #define SIXAXIS_CONTROLLER_USB  (1 << 1)
 #define SIXAXIS_CONTROLLER_BT   (1 << 2)
+#define CE_REMOTE_BT            (1 << 4)
+
+/* measured on real hardware */
+#define CE_REMOTE_MIN_X 0
+#define CE_REMOTE_MAX_X 1667
+#define CE_REMOTE_SIZE_X (float)48 /* size in mm */
+#define CE_REMOTE_MIN_Y 0
+#define CE_REMOTE_MAX_Y 1868
+#define CE_REMOTE_SIZE_Y (float)51
+#define CE_REMOTE_RES_X ((CE_REMOTE_MAX_X - CE_REMOTE_MIN_X) / CE_REMOTE_SIZE_X)
+#define CE_REMOTE_RES_Y ((CE_REMOTE_MAX_Y - CE_REMOTE_MIN_Y) / CE_REMOTE_SIZE_Y)
 
 static const u8 sixaxis_rdesc_fixup[] = {
 	0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C,
@@ -57,6 +70,7 @@ static const u8 sixaxis_rdesc_fixup2[] = {
 
 struct sony_sc {
 	unsigned long quirks;
+	struct input_dev *input;
 };
 
 /* Sony Vaio VGX has wrongly mouse pointer declared as constant */
@@ -94,10 +108,27 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 			 *rsize, (int)sizeof(sixaxis_rdesc_fixup2));
 		*rsize = sizeof(sixaxis_rdesc_fixup2);
 		memcpy(rdesc, &sixaxis_rdesc_fixup2, *rsize);
+	} else if ((sc->quirks & CE_REMOTE_BT) && *rsize == 359 &&
+			rdesc[358] == 0x0) {
+		hid_info(hdev, "Fixing up Sony CE Remote report descriptor\n");
+		*rsize = 358;
 	}
 	return rdesc;
 }
 
+static int sony_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+	struct hid_field *field, struct hid_usage *usage,
+	unsigned long **bit, int *max)
+{
+	struct sony_sc *sc = hid_get_drvdata(hdev);
+	if (sc->quirks & CE_REMOTE_BT) {
+		if (!sc->input)
+			sc->input = hi->input;
+	}
+
+	return 0;
+}
+
 static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
 		__u8 *rd, int size)
 {
@@ -112,8 +143,55 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
 		swap(rd[43], rd[44]);
 		swap(rd[45], rd[46]);
 		swap(rd[47], rd[48]);
+	} else if (sc->quirks & CE_REMOTE_BT) {
+		struct input_dev *input = sc->input;
+		if (!input) {
+			hid_err(hdev, "Sony CE Remote no input data structure");
+			return 0;
+		}
+		if (rd[0] == 0x2) { /* report id = trackpad */
+			__u8 button0 = rd[1] & 0x1;
+			__u8 button2 = (rd[1] & 0x4) >> 2;
+			__u8 contact0 = (rd[1] & 0x30) >> 4;
+			__u8 contact1 = (rd[1] & 0xc0) >> 6;
+			__u16 touch0x = rd[2] | (rd[3] & 0xf) << 8;
+			__u16 touch0y = (rd[3] & 0xf0) >> 4 | rd[4] << 4;
+			__u8 touch0w = rd[5] & 0xf;
+			__u8 touch0h = (rd[5] & 0xf0) >> 4;
+			int8_t xrel = rd[6];
+			__u16 touch1x = rd[7] | (rd[8] & 0xf) << 8;
+			__u16 touch1y = (rd[8] & 0xf0) >> 4 | rd[9] << 4;
+			__u8 touch1w = rd[10] & 0xf;
+			__u8 touch1h = (rd[10] & 0xf0) >> 4;
+			int8_t yrel = rd[11];
+
+			input_mt_slot(input, 0);
+			input_mt_report_slot_state(input, MT_TOOL_FINGER, contact0);
+			/* flip Y-axis */
+			if (contact0) {
+				input_report_abs(input, ABS_MT_TOUCH_MAJOR, max(touch0w, touch0h));
+				input_report_abs(input, ABS_MT_TOUCH_MINOR, min(touch0w, touch0h));
+				input_report_abs(input, ABS_MT_ORIENTATION, (bool)(touch0w > touch0h));
+				input_report_abs(input, ABS_MT_POSITION_X, touch0x);
+				input_report_abs(input, ABS_MT_POSITION_Y, CE_REMOTE_MAX_Y - touch0y);
+			}
+			input_mt_slot(input, 1);
+			input_mt_report_slot_state(input, MT_TOOL_FINGER, contact1);
+			if (contact1) {
+				input_report_abs(input, ABS_MT_TOUCH_MAJOR, max(touch1w, touch1h));
+				input_report_abs(input, ABS_MT_TOUCH_MINOR, min(touch1w, touch1h));
+				input_report_abs(input, ABS_MT_ORIENTATION, (bool)(touch1w > touch1h));
+				input_report_abs(input, ABS_MT_POSITION_X, touch1x);
+				input_report_abs(input, ABS_MT_POSITION_Y, CE_REMOTE_MAX_Y - touch1y);
+			}
+			input_report_rel(input, REL_X, xrel);
+			input_report_rel(input, REL_Y, yrel);
+
+			input_report_key(input, BTN_MOUSE, button0 | button2);
+			input_mt_report_pointer_emulation(input, true);
+		}
+		input_sync(input);
 	}
-
 	return 0;
 }
 
@@ -192,6 +270,43 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev)
 	return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
 }
 
+static int ce_remote_setup_input(struct input_dev *input, struct hid_device *hdev)
+{
+	const int FUZZ = 4;
+	int error;
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(EV_REL, input->evbit);
+	__set_bit(EV_ABS, input->evbit);
+	__clear_bit(BTN_RIGHT, input->keybit);
+	__clear_bit(BTN_MIDDLE, input->keybit);
+	__set_bit(BTN_MOUSE, input->keybit);
+	__set_bit(BTN_TOOL_FINGER, input->keybit);
+	__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
+	__set_bit(BTN_TOUCH, input->keybit);
+	__set_bit(INPUT_PROP_POINTER, input->propbit);
+	__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+
+	error = input_mt_init_slots(input, 2, 0);
+	if (error)
+		return error;
+
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 15, FUZZ, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 15, FUZZ, 0);
+	input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+
+	input_set_abs_params(input, ABS_X, CE_REMOTE_MIN_X, CE_REMOTE_MAX_X, FUZZ, 0);
+	input_set_abs_params(input, ABS_Y, CE_REMOTE_MIN_Y, CE_REMOTE_MAX_Y, FUZZ, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_X, CE_REMOTE_MIN_X, CE_REMOTE_MAX_X, FUZZ, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, CE_REMOTE_MIN_Y, CE_REMOTE_MAX_Y, FUZZ, 0);
+
+	input_abs_set_res(input, ABS_X, CE_REMOTE_RES_X);
+	input_abs_set_res(input, ABS_Y, CE_REMOTE_RES_Y);
+	input_abs_set_res(input, ABS_MT_POSITION_X, CE_REMOTE_RES_X);
+	input_abs_set_res(input, ABS_MT_POSITION_Y, CE_REMOTE_RES_Y);
+
+	return 0;
+}
+
 static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
 	int ret;
@@ -226,6 +341,15 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	}
 	else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
 		ret = sixaxis_set_operational_bt(hdev);
+	else if (sc->quirks & CE_REMOTE_BT) {
+		if (sc->input) {
+			ret = ce_remote_setup_input(sc->input, hdev);
+			if (ret) {
+				hid_err(hdev, "Sony Touch Remote setup input failed (%d)\n", ret);
+				goto err_stop;
+			}
+		}
+	}
 	else
 		ret = 0;
 
@@ -257,6 +381,10 @@ static const struct hid_device_id sony_devices[] = {
 		.driver_data = VAIO_RDESC_CONSTANT },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE),
 		.driver_data = VAIO_RDESC_CONSTANT },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY_TOUCH_REMOTE, USB_DEVICE_ID_SONY_TOUCH_REMOTE_LYRA),
+		.driver_data = CE_REMOTE_BT },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY_TOUCH_REMOTE, USB_DEVICE_ID_SONY_TOUCH_REMOTE_LEO),
+		.driver_data = CE_REMOTE_BT },
 	{ }
 };
 MODULE_DEVICE_TABLE(hid, sony_devices);
@@ -266,6 +394,7 @@ static struct hid_driver sony_driver = {
 	.id_table = sony_devices,
 	.probe = sony_probe,
 	.remove = sony_remove,
+	.input_mapping = sony_input_mapping,
 	.report_fixup = sony_report_fixup,
 	.raw_event = sony_raw_event
 };

[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux