diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 18933074e59a..77814cbe2d4c 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -964,6 +964,9 @@ #define USB_VENDOR_ID_SMK 0x0609 #define USB_DEVICE_ID_SMK_PS3_BDREMOTE 0x0306 +#define USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE 0x0368 +#define USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE 0x0369 + #define USB_VENDOR_ID_SONY 0x054c #define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 0176a927070a..c691d332f549 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -614,6 +614,8 @@ static const struct hid_device_id hid_have_special_driver[] = { #if IS_ENABLED(CONFIG_HID_SONY) { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) }, diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index ccdc5f2d01b1..e475c5073c99 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -9,6 +9,7 @@ * Copyright (c) 2006-2013 Jiri Kosina * Copyright (c) 2013 Colin Leitner * Copyright (c) 2014-2016 Frank Praznik + * Copyright (c) 2018 Todd Kelner */ /* @@ -55,6 +56,8 @@ #define NAVIGATION_CONTROLLER_BT BIT(11) #define SINO_LITE_CONTROLLER BIT(12) #define FUTUREMAX_DANCE_MAT BIT(13) +#define NSG_MR5U_REMOTE_BT BIT(14) +#define NSG_MR7U_REMOTE_BT BIT(15) #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT) #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT) @@ -72,8 +75,11 @@ MOTION_CONTROLLER) #define SONY_BT_DEVICE (SIXAXIS_CONTROLLER_BT | DUALSHOCK4_CONTROLLER_BT |\ MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER_BT) +#define NSG_MRXU_REMOTE (NSG_MR5U_REMOTE_BT | NSG_MR7U_REMOTE_BT) #define MAX_LEDS 4 +#define NSG_MRXU_MAX_X 1667 +#define NSG_MRXU_MAX_Y 1868 /* PS/3 Motion controller */ @@ -1098,6 +1104,80 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size) } } +static void nsg_mrxu_parse_report(struct sony_sc *sc, u8 *rd, int size) +{ + int n, offset, relx, rely; + u8 active; + + /* + * The NSG-MRxU multi-touch trackpad data starts at offset 1 and + * the touch-related data starts at offset 2. + * For the first byte, bit 0 is set when touchpad button is pressed. + * Bit 2 is set when a touch is active and the drag (Fn) key is pressed. + * This drag key is mapped to BTN_LEFT. It is operational only when a + * touch point is active. + * Bit 4 is set when only the first touch point is active. + * Bit 6 is set when only the second touch point is active. + * Bits 5 and 7 are set when both touch points are active. + * The next 3 bytes are two 12 bit X/Y coordinates for the first touch. + * The following byte, offset 5, has the touch width and length. + * Bits 0-4=X (width), bits 5-7=Y (length). + * A signed relative X coordinate is at offset 6. + * The bytes at offset 7-9 are the second touch X/Y coordinates. + * Offset 10 has the second touch width and length. + * Offset 11 has the relative Y coordinate. + */ + offset = 1; + + input_report_key(sc->touchpad, BTN_LEFT, rd[offset] & 0x0F); + active = (rd[offset] >> 4); + relx = (s8) rd[offset+5]; + rely = ((s8) rd[offset+10]) * -1; + + offset++; + + for (n = 0; n < 2; n++) { + u16 x, y; + u8 contactx, contacty; + + x = rd[offset] | ((rd[offset+1] & 0x0F) << 8); + y = ((rd[offset+1] & 0xF0) >> 4) | (rd[offset+2] << 4); + + input_mt_slot(sc->touchpad, n); + input_mt_report_slot_state(sc->touchpad, MT_TOOL_FINGER, active & 0x03); + + if (active & 0x03) { + contactx = rd[offset+3] & 0x0F; + contacty = rd[offset+3] >> 4; + input_report_abs(sc->touchpad, ABS_MT_TOUCH_MAJOR, + max(contactx, contacty)); + input_report_abs(sc->touchpad, ABS_MT_TOUCH_MINOR, + min(contactx, contacty)); + input_report_abs(sc->touchpad, ABS_MT_ORIENTATION, + (bool) (contactx > contacty)); + input_report_abs(sc->touchpad, ABS_MT_POSITION_X, x); + input_report_abs(sc->touchpad, ABS_MT_POSITION_Y, + NSG_MRXU_MAX_Y - y); + /* + * The relative coordinates belong to the first touch + * point, when present, or to the second touch point + * when the first is not active. + */ + if ((n == 0) || ((n == 1) && (active & 0x01))) { + input_report_rel(sc->touchpad, REL_X, relx); + input_report_rel(sc->touchpad, REL_Y, rely); + } + } + + offset += 5; + active >>= 2; + } + + input_mt_sync_frame(sc->touchpad); + + input_sync(sc->touchpad); +} + static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *rd, int size) { @@ -1206,6 +1286,10 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, } dualshock4_parse_report(sc, rd, size); + + } else if ((sc->quirks & NSG_MRXU_REMOTE) && rd[0] == 0x02) { + nsg_mrxu_parse_report(sc, rd, size); + return 1; } if (sc->defer_initialization) { @@ -1263,7 +1347,7 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, } static int sony_register_touchpad(struct sony_sc *sc, int touch_count, - int w, int h) + int w, int h, int touch_major, int touch_minor, int orientation) { size_t name_sz; char *name; @@ -1294,10 +1378,6 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count, snprintf(name, name_sz, "%s" DS4_TOUCHPAD_SUFFIX, sc->hdev->name); sc->touchpad->name = name; - ret = input_mt_init_slots(sc->touchpad, touch_count, INPUT_MT_POINTER); - if (ret < 0) - goto err; - /* We map the button underneath the touchpad to BTN_LEFT. */ __set_bit(EV_KEY, sc->touchpad->evbit); __set_bit(BTN_LEFT, sc->touchpad->keybit); @@ -1306,6 +1386,25 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count, input_set_abs_params(sc->touchpad, ABS_MT_POSITION_X, 0, w, 0, 0); input_set_abs_params(sc->touchpad, ABS_MT_POSITION_Y, 0, h, 0, 0); + if (touch_major > 0) { + input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MAJOR, + 0, touch_major, 0, 0); + if (touch_minor > 0) + input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MINOR, + 0, touch_minor, 0, 0); + if (orientation > 0) + input_set_abs_params(sc->touchpad, ABS_MT_ORIENTATION, + 0, orientation, 0, 0); + } + + if (sc->quirks & NSG_MRXU_REMOTE) { + __set_bit(EV_REL, sc->touchpad->evbit); + } + + ret = input_mt_init_slots(sc->touchpad, touch_count, INPUT_MT_POINTER); + if (ret < 0) + goto err; + ret = input_register_device(sc->touchpad); if (ret < 0) goto err; @@ -2690,7 +2789,7 @@ static int sony_input_configured(struct hid_device *hdev, * The Dualshock 4 touchpad supports 2 touches and has a * resolution of 1920x942 (44.86 dots/mm). */ - ret = sony_register_touchpad(sc, 2, 1920, 942); + ret = sony_register_touchpad(sc, 2, 1920, 942, 0, 0, 0); if (ret) { hid_err(sc->hdev, "Unable to initialize multi-touch slots: %d\n", @@ -2721,6 +2820,20 @@ static int sony_input_configured(struct hid_device *hdev, } sony_init_output_report(sc, dualshock4_send_output_report); + } else if (sc->quirks & NSG_MRXU_REMOTE) { + /* + * The NSG-MRxU touchpad supports 2 touches and has a + * resolution of 1667x1868 + */ + ret = sony_register_touchpad(sc, 2, + NSG_MRXU_MAX_X, NSG_MRXU_MAX_Y, 15, 15, 1); + if (ret) { + hid_err(sc->hdev, + "Unable to initialize multi-touch slots: %d\n", + ret); + goto err_stop; + } + } else if (sc->quirks & MOTION_CONTROLLER) { sony_init_output_report(sc, motion_send_output_report); } else { @@ -2969,6 +3082,12 @@ static const struct hid_device_id sony_devices[] = { /* Nyko Core Controller for PS3 */ { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER), .driver_data = SIXAXIS_CONTROLLER_USB | SINO_LITE_CONTROLLER }, + /* SMK-Link NSG-MR5U Remote Control */ + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE), + .driver_data = NSG_MR5U_REMOTE_BT }, + /* SMK-Link NSG-MR7U Remote Control */ + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE), + .driver_data = NSG_MR7U_REMOTE_BT }, { } }; MODULE_DEVICE_TABLE(hid, sony_devices);