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 };