Merge branches 'for-3.19/hid-report-len', 'for-3.19/i2c-hid', 'for-3.19/lenovo',...
authorJiri Kosina <jkosina@suse.cz>
Fri, 12 Dec 2014 10:15:33 +0000 (11:15 +0100)
committerJiri Kosina <jkosina@suse.cz>
Fri, 12 Dec 2014 10:15:33 +0000 (11:15 +0100)
26 files changed:
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-core.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-lenovo.c
drivers/hid/hid-logitech-dj.c
drivers/hid/hid-logitech-dj.h [deleted file]
drivers/hid/hid-logitech-hidpp.c [new file with mode: 0644]
drivers/hid/hid-microsoft.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-plantronics.c [new file with mode: 0644]
drivers/hid/hid-rmi.c
drivers/hid/hid-roccat-kone.c
drivers/hid/hid-saitek.c
drivers/hid/hid-sony.c
drivers/hid/i2c-hid/i2c-hid.c
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hid-quirks.c
drivers/hid/usbhid/usbhid.h
drivers/hid/wacom.h
drivers/hid/wacom_sys.c
drivers/hid/wacom_wac.c
drivers/hid/wacom_wac.h
drivers/usb/core/quirks.c
include/linux/hid.h

index f42df4d..230b6f8 100644 (file)
@@ -371,6 +371,7 @@ config HID_LOGITECH_DJ
        tristate "Logitech Unifying receivers full support"
        depends on HIDRAW
        depends on HID_LOGITECH
+       select HID_LOGITECH_HIDPP
        ---help---
        Say Y if you want support for Logitech Unifying receivers and devices.
        Unifying receivers are capable of pairing up to 6 Logitech compliant
@@ -378,6 +379,17 @@ config HID_LOGITECH_DJ
        generic USB_HID driver and all incoming events will be multiplexed
        into a single mouse and a single keyboard device.
 
+config HID_LOGITECH_HIDPP
+       tristate "Logitech HID++ devices support"
+       depends on HID_LOGITECH
+       ---help---
+       Support for Logitech devices relyingon the HID++ Logitech specification
+
+       Say Y if you want support for Logitech devices relying on the HID++
+       specification. Such devices are the various Logitech Touchpads (T650,
+       T651, TK820), some mice (Zone Touch mouse), or even keyboards (Solar
+       Keayboard).
+
 config LOGITECH_FF
        bool "Logitech force feedback support"
        depends on HID_LOGITECH
@@ -613,6 +625,13 @@ config HID_PICOLCD_CIR
        ---help---
          Provide access to PicoLCD's CIR interface via remote control (LIRC).
 
+config HID_PLANTRONICS
+       tristate "Plantronics USB HID Driver"
+       default !EXPERT
+       depends on HID
+       ---help---
+       Provides HID support for Plantronics telephony devices.
+
 config HID_PRIMAX
        tristate "Primax non-fully HID-compliant devices"
        depends on HID
@@ -629,7 +648,7 @@ config HID_ROCCAT
        support for its special functionalities.
 
 config HID_SAITEK
-       tristate "Saitek non-fully HID-compliant devices"
+       tristate "Saitek (Mad Catz) non-fully HID-compliant devices"
        depends on HID
        ---help---
        Support for Saitek devices that are not fully compliant with the
@@ -637,6 +656,7 @@ config HID_SAITEK
 
        Supported devices:
        - PS1000 Dual Analog Pad
+       - R.A.T.9 Gaming Mouse
        - R.A.T.7 Gaming Mouse
        - M.M.O.7 Gaming Mouse
 
index e2850d8..debd15b 100644 (file)
@@ -63,6 +63,7 @@ obj-$(CONFIG_HID_LCPOWER)       += hid-lcpower.o
 obj-$(CONFIG_HID_LENOVO)       += hid-lenovo.o
 obj-$(CONFIG_HID_LOGITECH)     += hid-logitech.o
 obj-$(CONFIG_HID_LOGITECH_DJ)  += hid-logitech-dj.o
+obj-$(CONFIG_HID_LOGITECH_HIDPP)       += hid-logitech-hidpp.o
 obj-$(CONFIG_HID_MAGICMOUSE)    += hid-magicmouse.o
 obj-$(CONFIG_HID_MICROSOFT)    += hid-microsoft.o
 obj-$(CONFIG_HID_MONTEREY)     += hid-monterey.o
@@ -94,6 +95,7 @@ ifdef CONFIG_DEBUG_FS
 hid-picolcd-y                  += hid-picolcd_debugfs.o
 endif
 
+obj-$(CONFIG_HID_PLANTRONICS)  += hid-plantronics.o
 obj-$(CONFIG_HID_PRIMAX)       += hid-primax.o
 obj-$(CONFIG_HID_ROCCAT)       += hid-roccat.o hid-roccat-common.o \
        hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
index 3402033..c3d0ac1 100644 (file)
@@ -702,6 +702,11 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
        if (((parser->global.usage_page << 16) == HID_UP_SENSOR) &&
            type == HID_COLLECTION_PHYSICAL)
                hid->group = HID_GROUP_SENSOR_HUB;
+
+       if (hid->vendor == USB_VENDOR_ID_MICROSOFT &&
+           hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 &&
+           hid->group == HID_GROUP_MULTITOUCH)
+               hid->group = HID_GROUP_GENERIC;
 }
 
 static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
@@ -779,16 +784,6 @@ static int hid_scan_report(struct hid_device *hid)
            (hid->group == HID_GROUP_MULTITOUCH))
                hid->group = HID_GROUP_MULTITOUCH_WIN_8;
 
-       /*
-       * Vendor specific handlings
-       */
-       if ((hid->vendor == USB_VENDOR_ID_SYNAPTICS) &&
-           (hid->group == HID_GROUP_GENERIC) &&
-           /* only bind to the mouse interface of composite USB devices */
-           (hid->bus != BUS_USB || hid->type == HID_TYPE_USBMOUSE))
-               /* hid-rmi should take care of them, not hid-generic */
-               hid->group = HID_GROUP_RMI;
-
        /*
         * Vendor specific handlings
         */
@@ -796,6 +791,13 @@ static int hid_scan_report(struct hid_device *hid)
        case USB_VENDOR_ID_WACOM:
                hid->group = HID_GROUP_WACOM;
                break;
+       case USB_VENDOR_ID_SYNAPTICS:
+               if ((hid->group == HID_GROUP_GENERIC) &&
+                   (hid->bus != BUS_USB || hid->type == HID_TYPE_USBMOUSE))
+                       /* hid-rmi should only bind to the mouse interface of
+                        * composite USB devices */
+                       hid->group = HID_GROUP_RMI;
+               break;
        }
 
        vfree(parser);
@@ -1280,12 +1282,6 @@ void hid_output_report(struct hid_report *report, __u8 *data)
 }
 EXPORT_SYMBOL_GPL(hid_output_report);
 
-static int hid_report_len(struct hid_report *report)
-{
-       /* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */
-       return ((report->size - 1) >> 3) + 1 + (report->id > 0);
-}
-
 /*
  * Allocator for buffer that is going to be passed to hid_output_report()
  */
@@ -1822,6 +1818,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) },
@@ -1862,6 +1859,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
        { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
@@ -1887,6 +1885,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
        { HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_6000) },
        { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) },
        { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
 #if IS_ENABLED(CONFIG_HID_ROCCAT)
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
@@ -1910,10 +1909,12 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9) },
 #endif
        { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
        { 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_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
@@ -2539,7 +2540,8 @@ int hid_add_device(struct hid_device *hdev)
         * Scan generic devices for group information
         */
        if (hid_ignore_special_drivers ||
-           !hid_match_id(hdev, hid_have_special_driver)) {
+           (!hdev->group &&
+            !hid_match_id(hdev, hid_have_special_driver))) {
                ret = hid_scan_report(hdev);
                if (ret)
                        hid_warn(hdev, "bad device descriptor (%d)\n", ret);
index 7c86373..7460f34 100644 (file)
 #define USB_DEVICE_ID_ELAN_TOUCHSCREEN 0x0089
 #define USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B    0x009b
 #define USB_DEVICE_ID_ELAN_TOUCHSCREEN_0103    0x0103
+#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_010c    0x010c
 #define USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F    0x016f
 
 #define USB_VENDOR_ID_ELECOM           0x056e
 
 #define USB_VENDOR_ID_LOGITECH         0x046d
 #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
+#define USB_DEVICE_ID_LOGITECH_T651    0xb00c
 #define USB_DEVICE_ID_LOGITECH_RECEIVER        0xc101
 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST  0xc110
 #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
 
 #define USB_VENDOR_ID_MADCATZ          0x0738
 #define USB_DEVICE_ID_MADCATZ_BEATPAD  0x4540
+#define USB_DEVICE_ID_MADCATZ_RAT9     0x1709
 
 #define USB_VENDOR_ID_MCC              0x09db
 #define USB_DEVICE_ID_MCC_PMD1024LS    0x0076
 #define USB_DEVICE_ID_MS_SURFACE_PRO_2   0x0799
 #define USB_DEVICE_ID_MS_TOUCH_COVER_2   0x07a7
 #define USB_DEVICE_ID_MS_TYPE_COVER_2    0x07a9
+#define USB_DEVICE_ID_MS_TYPE_COVER_3    0x07dc
 
 #define USB_VENDOR_ID_MOJO             0x8282
 #define USB_DEVICE_ID_RETRO_ADAPTER    0x3201
 #define USB_DEVICE_ID_ORTEK_PKB1700    0x1700
 #define USB_DEVICE_ID_ORTEK_WKB2000    0x2000
 
+#define USB_VENDOR_ID_PLANTRONICS      0x047f
+
 #define USB_VENDOR_ID_PANASONIC                0x04da
 #define USB_DEVICE_ID_PANABOARD_UBT780 0x1044
 #define USB_DEVICE_ID_PANABOARD_UBT880 0x104d
 #define USB_VENDOR_ID_SKYCABLE                 0x1223
 #define        USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER       0x3F07
 
+#define USB_VENDOR_ID_SMK              0x0609
+#define USB_DEVICE_ID_SMK_PS3_BDREMOTE 0x0306
+
 #define USB_VENDOR_ID_SONY                     0x054c
 #define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE      0x024b
 #define USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE      0x0374
 #define USB_DEVICE_ID_VERNIER_CYCLOPS  0x0004
 #define USB_DEVICE_ID_VERNIER_LCSPEC   0x0006
 
+#define USB_VENDOR_ID_VTL              0x0306
+#define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F      0xff3f
+
 #define USB_VENDOR_ID_WACOM            0x056a
 #define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81
 #define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH   0x00BD
index 725f22c..e0a0f06 100644 (file)
@@ -872,7 +872,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x2cb: map_key_clear(KEY_KBDINPUTASSIST_ACCEPT);   break;
                case 0x2cc: map_key_clear(KEY_KBDINPUTASSIST_CANCEL);   break;
 
-               default:    goto ignore;
+               default: map_key_clear(KEY_UNKNOWN);
                }
                break;
 
@@ -1215,7 +1215,7 @@ static void hidinput_led_worker(struct work_struct *work)
                return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT);
 
        /* fall back to generic raw-output-report */
-       len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+       len = hid_report_len(report);
        buf = hid_alloc_report_buf(report, GFP_KERNEL);
        if (!buf)
                return;
index bf227f7..4c55f4d 100644 (file)
@@ -62,7 +62,6 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev,
        /* HID_UP_LNVENDOR = USB, HID_UP_MSVENDOR = BT */
        if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR ||
            (usage->hid & HID_USAGE_PAGE) == HID_UP_LNVENDOR) {
-               set_bit(EV_REP, hi->input->evbit);
                switch (usage->hid & HID_USAGE) {
                case 0x00f1: /* Fn-F4: Mic mute */
                        map_key_clear(KEY_MICMUTE);
@@ -85,13 +84,13 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev,
                case 0x00f8: /* Fn-F11: View open applications (3 boxes) */
                        map_key_clear(KEY_SCALE);
                        return 1;
-               case 0x00fa: /* Fn-Esc: Fn-lock toggle */
-                       map_key_clear(KEY_FN_ESC);
-                       return 1;
-               case 0x00fb: /* Fn-F12: Open My computer (6 boxes) USB-only */
+               case 0x00f9: /* Fn-F12: Open My computer (6 boxes) USB-only */
                        /* NB: This mapping is invented in raw_event below */
                        map_key_clear(KEY_FILE);
                        return 1;
+               case 0x00fa: /* Fn-Esc: Fn-lock toggle */
+                       map_key_clear(KEY_FN_ESC);
+                       return 1;
                }
        }
 
@@ -207,8 +206,8 @@ static int lenovo_raw_event(struct hid_device *hdev,
                        && data[0] == 0x15
                        && data[1] == 0x94
                        && data[2] == 0x01)) {
-               data[1] = 0x0;
-               data[2] = 0x4;
+               data[1] = 0x00;
+               data[2] = 0x01;
        }
 
        return 0;
index 71f5692..c917ab6 100644 (file)
 #include <linux/hid.h>
 #include <linux/module.h>
 #include <linux/usb.h>
+#include <linux/kfifo.h>
 #include <asm/unaligned.h>
 #include "hid-ids.h"
-#include "hid-logitech-dj.h"
+
+#define DJ_MAX_PAIRED_DEVICES                  6
+#define DJ_MAX_NUMBER_NOTIFICATIONS            8
+#define DJ_RECEIVER_INDEX                      0
+#define DJ_DEVICE_INDEX_MIN                    1
+#define DJ_DEVICE_INDEX_MAX                    6
+
+#define DJREPORT_SHORT_LENGTH                  15
+#define DJREPORT_LONG_LENGTH                   32
+
+#define REPORT_ID_DJ_SHORT                     0x20
+#define REPORT_ID_DJ_LONG                      0x21
+
+#define REPORT_ID_HIDPP_SHORT                  0x10
+#define REPORT_ID_HIDPP_LONG                   0x11
+
+#define HIDPP_REPORT_SHORT_LENGTH              7
+#define HIDPP_REPORT_LONG_LENGTH               20
+
+#define HIDPP_RECEIVER_INDEX                   0xff
+
+#define REPORT_TYPE_RFREPORT_FIRST             0x01
+#define REPORT_TYPE_RFREPORT_LAST              0x1F
+
+/* Command Switch to DJ mode */
+#define REPORT_TYPE_CMD_SWITCH                 0x80
+#define CMD_SWITCH_PARAM_DEVBITFIELD           0x00
+#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS       0x01
+#define TIMEOUT_NO_KEEPALIVE                   0x00
+
+/* Command to Get the list of Paired devices */
+#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES     0x81
+
+/* Device Paired Notification */
+#define REPORT_TYPE_NOTIF_DEVICE_PAIRED                0x41
+#define SPFUNCTION_MORE_NOTIF_EXPECTED         0x01
+#define SPFUNCTION_DEVICE_LIST_EMPTY           0x02
+#define DEVICE_PAIRED_PARAM_SPFUNCTION         0x00
+#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB       0x01
+#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB       0x02
+#define DEVICE_PAIRED_RF_REPORT_TYPE           0x03
+
+/* Device Un-Paired Notification */
+#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED      0x40
+
+
+/* Connection Status Notification */
+#define REPORT_TYPE_NOTIF_CONNECTION_STATUS    0x42
+#define CONNECTION_STATUS_PARAM_STATUS         0x00
+#define STATUS_LINKLOSS                                0x01
+
+/* Error Notification */
+#define REPORT_TYPE_NOTIF_ERROR                        0x7F
+#define NOTIF_ERROR_PARAM_ETYPE                        0x00
+#define ETYPE_KEEPALIVE_TIMEOUT                        0x01
+
+/* supported DJ HID && RF report types */
+#define REPORT_TYPE_KEYBOARD                   0x01
+#define REPORT_TYPE_MOUSE                      0x02
+#define REPORT_TYPE_CONSUMER_CONTROL           0x03
+#define REPORT_TYPE_SYSTEM_CONTROL             0x04
+#define REPORT_TYPE_MEDIA_CENTER               0x08
+#define REPORT_TYPE_LEDS                       0x0E
+
+/* RF Report types bitfield */
+#define STD_KEYBOARD                           0x00000002
+#define STD_MOUSE                              0x00000004
+#define MULTIMEDIA                             0x00000008
+#define POWER_KEYS                             0x00000010
+#define MEDIA_CENTER                           0x00000100
+#define KBD_LEDS                               0x00004000
+
+struct dj_report {
+       u8 report_id;
+       u8 device_index;
+       u8 report_type;
+       u8 report_params[DJREPORT_SHORT_LENGTH - 3];
+};
+
+struct dj_receiver_dev {
+       struct hid_device *hdev;
+       struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES +
+                                           DJ_DEVICE_INDEX_MIN];
+       struct work_struct work;
+       struct kfifo notif_fifo;
+       spinlock_t lock;
+       bool querying_devices;
+};
+
+struct dj_device {
+       struct hid_device *hdev;
+       struct dj_receiver_dev *dj_receiver_dev;
+       u32 reports_supported;
+       u8 device_index;
+};
 
 /* Keyboard descriptor (1) */
 static const char kbd_descriptor[] = {
@@ -156,6 +251,57 @@ static const char media_descriptor[] = {
        0xc0,                   /* EndCollection                       */
 };                             /*                                     */
 
+/* HIDPP descriptor */
+static const char hidpp_descriptor[] = {
+       0x06, 0x00, 0xff,       /* Usage Page (Vendor Defined Page 1)  */
+       0x09, 0x01,             /* Usage (Vendor Usage 1)              */
+       0xa1, 0x01,             /* Collection (Application)            */
+       0x85, 0x10,             /*   Report ID (16)                    */
+       0x75, 0x08,             /*   Report Size (8)                   */
+       0x95, 0x06,             /*   Report Count (6)                  */
+       0x15, 0x00,             /*   Logical Minimum (0)               */
+       0x26, 0xff, 0x00,       /*   Logical Maximum (255)             */
+       0x09, 0x01,             /*   Usage (Vendor Usage 1)            */
+       0x81, 0x00,             /*   Input (Data,Arr,Abs)              */
+       0x09, 0x01,             /*   Usage (Vendor Usage 1)            */
+       0x91, 0x00,             /*   Output (Data,Arr,Abs)             */
+       0xc0,                   /* End Collection                      */
+       0x06, 0x00, 0xff,       /* Usage Page (Vendor Defined Page 1)  */
+       0x09, 0x02,             /* Usage (Vendor Usage 2)              */
+       0xa1, 0x01,             /* Collection (Application)            */
+       0x85, 0x11,             /*   Report ID (17)                    */
+       0x75, 0x08,             /*   Report Size (8)                   */
+       0x95, 0x13,             /*   Report Count (19)                 */
+       0x15, 0x00,             /*   Logical Minimum (0)               */
+       0x26, 0xff, 0x00,       /*   Logical Maximum (255)             */
+       0x09, 0x02,             /*   Usage (Vendor Usage 2)            */
+       0x81, 0x00,             /*   Input (Data,Arr,Abs)              */
+       0x09, 0x02,             /*   Usage (Vendor Usage 2)            */
+       0x91, 0x00,             /*   Output (Data,Arr,Abs)             */
+       0xc0,                   /* End Collection                      */
+       0x06, 0x00, 0xff,       /* Usage Page (Vendor Defined Page 1)  */
+       0x09, 0x04,             /* Usage (Vendor Usage 0x04)           */
+       0xa1, 0x01,             /* Collection (Application)            */
+       0x85, 0x20,             /*   Report ID (32)                    */
+       0x75, 0x08,             /*   Report Size (8)                   */
+       0x95, 0x0e,             /*   Report Count (14)                 */
+       0x15, 0x00,             /*   Logical Minimum (0)               */
+       0x26, 0xff, 0x00,       /*   Logical Maximum (255)             */
+       0x09, 0x41,             /*   Usage (Vendor Usage 0x41)         */
+       0x81, 0x00,             /*   Input (Data,Arr,Abs)              */
+       0x09, 0x41,             /*   Usage (Vendor Usage 0x41)         */
+       0x91, 0x00,             /*   Output (Data,Arr,Abs)             */
+       0x85, 0x21,             /*   Report ID (33)                    */
+       0x95, 0x1f,             /*   Report Count (31)                 */
+       0x15, 0x00,             /*   Logical Minimum (0)               */
+       0x26, 0xff, 0x00,       /*   Logical Maximum (255)             */
+       0x09, 0x42,             /*   Usage (Vendor Usage 0x42)         */
+       0x81, 0x00,             /*   Input (Data,Arr,Abs)              */
+       0x09, 0x42,             /*   Usage (Vendor Usage 0x42)         */
+       0x91, 0x00,             /*   Output (Data,Arr,Abs)             */
+       0xc0,                   /* End Collection                      */
+};
+
 /* Maximum size of all defined hid reports in bytes (including report id) */
 #define MAX_REPORT_SIZE 8
 
@@ -165,7 +311,8 @@ static const char media_descriptor[] = {
         sizeof(mse_descriptor) +               \
         sizeof(consumer_descriptor) +          \
         sizeof(syscontrol_descriptor) +        \
-        sizeof(media_descriptor))
+        sizeof(media_descriptor) +     \
+        sizeof(hidpp_descriptor))
 
 /* Number of possible hid report types that can be created by this driver.
  *
@@ -256,11 +403,15 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
        dj_hiddev->dev.parent = &djrcv_hdev->dev;
        dj_hiddev->bus = BUS_USB;
        dj_hiddev->vendor = le16_to_cpu(usbdev->descriptor.idVendor);
-       dj_hiddev->product = le16_to_cpu(usbdev->descriptor.idProduct);
+       dj_hiddev->product =
+               (dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB]
+                                                                       << 8) |
+               dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB];
        snprintf(dj_hiddev->name, sizeof(dj_hiddev->name),
-               "Logitech Unifying Device. Wireless PID:%02x%02x",
-               dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB],
-               dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB]);
+               "Logitech Unifying Device. Wireless PID:%04x",
+               dj_hiddev->product);
+
+       dj_hiddev->group = HID_GROUP_LOGITECH_DJ_DEVICE;
 
        usb_make_path(usbdev, dj_hiddev->phys, sizeof(dj_hiddev->phys));
        snprintf(tmpstr, sizeof(tmpstr), ":%d", dj_report->device_index);
@@ -422,6 +573,13 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
        }
 }
 
+static void logi_dj_recv_forward_hidpp(struct dj_device *dj_dev, u8 *data,
+                                      int size)
+{
+       /* We are called from atomic context (tasklet && djrcv->lock held) */
+       if (hid_input_report(dj_dev->hdev, HID_INPUT_REPORT, data, size, 1))
+               dbg_hid("hid_input_report error\n");
+}
 
 static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
                                    struct dj_report *dj_report)
@@ -472,7 +630,9 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
 static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
                                          unsigned timeout)
 {
+       struct hid_device *hdev = djrcv_dev->hdev;
        struct dj_report *dj_report;
+       u8 *buf;
        int retval;
 
        dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
@@ -484,7 +644,6 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
        dj_report->report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x3F;
        dj_report->report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] = (u8)timeout;
        retval = logi_dj_recv_send_report(djrcv_dev, dj_report);
-       kfree(dj_report);
 
        /*
         * Ugly sleep to work around a USB 3.0 bug when the receiver is still
@@ -493,6 +652,30 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
         */
        msleep(50);
 
+       /*
+        * Magical bits to set up hidpp notifications when the dj devices
+        * are connected/disconnected.
+        *
+        * We can reuse dj_report because HIDPP_REPORT_SHORT_LENGTH is smaller
+        * than DJREPORT_SHORT_LENGTH.
+        */
+       buf = (u8 *)dj_report;
+
+       memset(buf, 0, HIDPP_REPORT_SHORT_LENGTH);
+
+       buf[0] = REPORT_ID_HIDPP_SHORT;
+       buf[1] = 0xFF;
+       buf[2] = 0x80;
+       buf[3] = 0x00;
+       buf[4] = 0x00;
+       buf[5] = 0x09;
+       buf[6] = 0x00;
+
+       hid_hw_raw_request(hdev, REPORT_ID_HIDPP_SHORT, buf,
+                       HIDPP_REPORT_SHORT_LENGTH, HID_OUTPUT_REPORT,
+                       HID_REQ_SET_REPORT);
+
+       kfree(dj_report);
        return retval;
 }
 
@@ -509,6 +692,9 @@ static void logi_dj_ll_close(struct hid_device *hid)
        dbg_hid("%s:%s\n", __func__, hid->phys);
 }
 
+static u8 unifying_name_query[]  = {0x10, 0xff, 0x83, 0xb5, 0x40, 0x00, 0x00};
+static u8 unifying_name_answer[] = {0x11, 0xff, 0x83, 0xb5};
+
 static int logi_dj_ll_raw_request(struct hid_device *hid,
                                  unsigned char reportnum, __u8 *buf,
                                  size_t count, unsigned char report_type,
@@ -519,6 +705,22 @@ static int logi_dj_ll_raw_request(struct hid_device *hid,
        u8 *out_buf;
        int ret;
 
+       if ((buf[0] == REPORT_ID_HIDPP_SHORT) ||
+           (buf[0] == REPORT_ID_HIDPP_LONG)) {
+               if (count < 2)
+                       return -EINVAL;
+
+               /* special case where we should not overwrite
+                * the device_index */
+               if (count == 7 && !memcmp(buf, unifying_name_query,
+                                         sizeof(unifying_name_query)))
+                       buf[4] |= djdev->device_index - 1;
+               else
+                       buf[1] = djdev->device_index;
+               return hid_hw_raw_request(djrcv_dev->hdev, reportnum, buf,
+                               count, report_type, reqtype);
+       }
+
        if (buf[0] != REPORT_TYPE_LEDS)
                return -EINVAL;
 
@@ -597,6 +799,8 @@ static int logi_dj_ll_parse(struct hid_device *hid)
                        __func__, djdev->reports_supported);
        }
 
+       rdcat(rdesc, &rsize, hidpp_descriptor, sizeof(hidpp_descriptor));
+
        retval = hid_parse_report(hid, rdesc, rsize);
        kfree(rdesc);
 
@@ -624,8 +828,7 @@ static struct hid_ll_driver logi_dj_ll_driver = {
        .raw_request = logi_dj_ll_raw_request,
 };
 
-
-static int logi_dj_raw_event(struct hid_device *hdev,
+static int logi_dj_dj_event(struct hid_device *hdev,
                             struct hid_report *report, u8 *data,
                             int size)
 {
@@ -633,36 +836,24 @@ static int logi_dj_raw_event(struct hid_device *hdev,
        struct dj_report *dj_report = (struct dj_report *) data;
        unsigned long flags;
 
-       dbg_hid("%s, size:%d\n", __func__, size);
-
-       /* Here we receive all data coming from iface 2, there are 4 cases:
-        *
-        * 1) Data should continue its normal processing i.e. data does not
-        * come from the DJ collection, in which case we do nothing and
-        * return 0, so hid-core can continue normal processing (will forward
-        * to associated hidraw device)
+       /*
+        * Here we receive all data coming from iface 2, there are 3 cases:
         *
-        * 2) Data is from DJ collection, and is intended for this driver i. e.
-        * data contains arrival, departure, etc notifications, in which case
-        * we queue them for delayed processing by the work queue. We return 1
-        * to hid-core as no further processing is required from it.
+        * 1) Data is intended for this driver i. e. data contains arrival,
+        * departure, etc notifications, in which case we queue them for delayed
+        * processing by the work queue. We return 1 to hid-core as no further
+        * processing is required from it.
         *
-        * 3) Data is from DJ collection, and informs a connection change,
-        * if the change means rf link loss, then we must send a null report
-        * to the upper layer to discard potentially pressed keys that may be
-        * repeated forever by the input layer. Return 1 to hid-core as no
-        * further processing is required.
+        * 2) Data informs a connection change, if the change means rf link
+        * loss, then we must send a null report to the upper layer to discard
+        * potentially pressed keys that may be repeated forever by the input
+        * layer. Return 1 to hid-core as no further processing is required.
         *
-        * 4) Data is from DJ collection and is an actual input event from
-        * a paired DJ device in which case we forward it to the correct hid
-        * device (via hid_input_report() ) and return 1 so hid-core does not do
-        * anything else with it.
+        * 3) Data is an actual input event from a paired DJ device in which
+        * case we forward it to the correct hid device (via hid_input_report()
+        * ) and return 1 so hid-core does not anything else with it.
         */
 
-       /* case 1) */
-       if (data[0] != REPORT_ID_DJ_SHORT)
-               return false;
-
        if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) ||
            (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) {
                /*
@@ -707,6 +898,80 @@ out:
        return true;
 }
 
+static int logi_dj_hidpp_event(struct hid_device *hdev,
+                            struct hid_report *report, u8 *data,
+                            int size)
+{
+       struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
+       struct dj_report *dj_report = (struct dj_report *) data;
+       unsigned long flags;
+       u8 device_index = dj_report->device_index;
+
+       if (device_index == HIDPP_RECEIVER_INDEX) {
+               /* special case were the device wants to know its unifying
+                * name */
+               if (size == HIDPP_REPORT_LONG_LENGTH &&
+                   !memcmp(data, unifying_name_answer,
+                           sizeof(unifying_name_answer)) &&
+                   ((data[4] & 0xF0) == 0x40))
+                       device_index = (data[4] & 0x0F) + 1;
+               else
+                       return false;
+       }
+
+       /*
+        * Data is from the HID++ collection, in this case, we forward the
+        * data to the corresponding child dj device and return 0 to hid-core
+        * so he data also goes to the hidraw device of the receiver. This
+        * allows a user space application to implement the full HID++ routing
+        * via the receiver.
+        */
+
+       if ((device_index < DJ_DEVICE_INDEX_MIN) ||
+           (device_index > DJ_DEVICE_INDEX_MAX)) {
+               /*
+                * Device index is wrong, bail out.
+                * This driver can ignore safely the receiver notifications,
+                * so ignore those reports too.
+                */
+               dev_err(&hdev->dev, "%s: invalid device index:%d\n",
+                               __func__, dj_report->device_index);
+               return false;
+       }
+
+       spin_lock_irqsave(&djrcv_dev->lock, flags);
+
+       if (!djrcv_dev->paired_dj_devices[device_index])
+               /* received an event for an unknown device, bail out */
+               goto out;
+
+       logi_dj_recv_forward_hidpp(djrcv_dev->paired_dj_devices[device_index],
+                                  data, size);
+
+out:
+       spin_unlock_irqrestore(&djrcv_dev->lock, flags);
+
+       return false;
+}
+
+static int logi_dj_raw_event(struct hid_device *hdev,
+                            struct hid_report *report, u8 *data,
+                            int size)
+{
+       dbg_hid("%s, size:%d\n", __func__, size);
+
+       switch (data[0]) {
+       case REPORT_ID_DJ_SHORT:
+               return logi_dj_dj_event(hdev, report, data, size);
+       case REPORT_ID_HIDPP_SHORT:
+               /* intentional fallthrough */
+       case REPORT_ID_HIDPP_LONG:
+               return logi_dj_hidpp_event(hdev, report, data, size);
+       }
+
+       return false;
+}
+
 static int logi_dj_probe(struct hid_device *hdev,
                         const struct hid_device_id *id)
 {
@@ -714,9 +979,6 @@ static int logi_dj_probe(struct hid_device *hdev,
        struct dj_receiver_dev *djrcv_dev;
        int retval;
 
-       if (is_dj_device((struct dj_device *)hdev->driver_data))
-               return -ENODEV;
-
        dbg_hid("%s called for ifnum %d\n", __func__,
                intf->cur_altsetting->desc.bInterfaceNumber);
 
@@ -869,22 +1131,6 @@ static void logi_dj_remove(struct hid_device *hdev)
        hid_set_drvdata(hdev, NULL);
 }
 
-static int logi_djdevice_probe(struct hid_device *hdev,
-                        const struct hid_device_id *id)
-{
-       int ret;
-       struct dj_device *dj_dev = hdev->driver_data;
-
-       if (!is_dj_device(dj_dev))
-               return -ENODEV;
-
-       ret = hid_parse(hdev);
-       if (!ret)
-               ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
-
-       return ret;
-}
-
 static const struct hid_device_id logi_dj_receivers[] = {
        {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
                USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)},
@@ -906,51 +1152,8 @@ static struct hid_driver logi_djreceiver_driver = {
 #endif
 };
 
+module_hid_driver(logi_djreceiver_driver);
 
-static const struct hid_device_id logi_dj_devices[] = {
-       {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
-               USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)},
-       {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
-               USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)},
-       {}
-};
-
-static struct hid_driver logi_djdevice_driver = {
-       .name = "logitech-djdevice",
-       .id_table = logi_dj_devices,
-       .probe = logi_djdevice_probe,
-};
-
-
-static int __init logi_dj_init(void)
-{
-       int retval;
-
-       dbg_hid("Logitech-DJ:%s\n", __func__);
-
-       retval = hid_register_driver(&logi_djreceiver_driver);
-       if (retval)
-               return retval;
-
-       retval = hid_register_driver(&logi_djdevice_driver);
-       if (retval)
-               hid_unregister_driver(&logi_djreceiver_driver);
-
-       return retval;
-
-}
-
-static void __exit logi_dj_exit(void)
-{
-       dbg_hid("Logitech-DJ:%s\n", __func__);
-
-       hid_unregister_driver(&logi_djdevice_driver);
-       hid_unregister_driver(&logi_djreceiver_driver);
-
-}
-
-module_init(logi_dj_init);
-module_exit(logi_dj_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Logitech");
 MODULE_AUTHOR("Nestor Lopez Casado");
diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h
deleted file mode 100644 (file)
index daeb0aa..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-#ifndef __HID_LOGITECH_DJ_H
-#define __HID_LOGITECH_DJ_H
-
-/*
- *  HID driver for Logitech Unifying receivers
- *
- *  Copyright (c) 2011 Logitech
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/kfifo.h>
-
-#define DJ_MAX_PAIRED_DEVICES                  6
-#define DJ_MAX_NUMBER_NOTIFICATIONS            8
-#define DJ_RECEIVER_INDEX                      0
-#define DJ_DEVICE_INDEX_MIN                    1
-#define DJ_DEVICE_INDEX_MAX                    6
-
-#define DJREPORT_SHORT_LENGTH                  15
-#define DJREPORT_LONG_LENGTH                   32
-
-#define REPORT_ID_DJ_SHORT                     0x20
-#define REPORT_ID_DJ_LONG                      0x21
-
-#define REPORT_TYPE_RFREPORT_FIRST             0x01
-#define REPORT_TYPE_RFREPORT_LAST              0x1F
-
-/* Command Switch to DJ mode */
-#define REPORT_TYPE_CMD_SWITCH                 0x80
-#define CMD_SWITCH_PARAM_DEVBITFIELD           0x00
-#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS       0x01
-#define TIMEOUT_NO_KEEPALIVE                   0x00
-
-/* Command to Get the list of Paired devices */
-#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES     0x81
-
-/* Device Paired Notification */
-#define REPORT_TYPE_NOTIF_DEVICE_PAIRED                0x41
-#define SPFUNCTION_MORE_NOTIF_EXPECTED         0x01
-#define SPFUNCTION_DEVICE_LIST_EMPTY           0x02
-#define DEVICE_PAIRED_PARAM_SPFUNCTION         0x00
-#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB       0x01
-#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB       0x02
-#define DEVICE_PAIRED_RF_REPORT_TYPE           0x03
-
-/* Device Un-Paired Notification */
-#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED      0x40
-
-
-/* Connection Status Notification */
-#define REPORT_TYPE_NOTIF_CONNECTION_STATUS    0x42
-#define CONNECTION_STATUS_PARAM_STATUS         0x00
-#define STATUS_LINKLOSS                                0x01
-
-/* Error Notification */
-#define REPORT_TYPE_NOTIF_ERROR                        0x7F
-#define NOTIF_ERROR_PARAM_ETYPE                        0x00
-#define ETYPE_KEEPALIVE_TIMEOUT                        0x01
-
-/* supported DJ HID && RF report types */
-#define REPORT_TYPE_KEYBOARD                   0x01
-#define REPORT_TYPE_MOUSE                      0x02
-#define REPORT_TYPE_CONSUMER_CONTROL           0x03
-#define REPORT_TYPE_SYSTEM_CONTROL             0x04
-#define REPORT_TYPE_MEDIA_CENTER               0x08
-#define REPORT_TYPE_LEDS                       0x0E
-
-/* RF Report types bitfield */
-#define STD_KEYBOARD                           0x00000002
-#define STD_MOUSE                              0x00000004
-#define MULTIMEDIA                             0x00000008
-#define POWER_KEYS                             0x00000010
-#define MEDIA_CENTER                           0x00000100
-#define KBD_LEDS                               0x00004000
-
-struct dj_report {
-       u8 report_id;
-       u8 device_index;
-       u8 report_type;
-       u8 report_params[DJREPORT_SHORT_LENGTH - 3];
-};
-
-struct dj_receiver_dev {
-       struct hid_device *hdev;
-       struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES +
-                                           DJ_DEVICE_INDEX_MIN];
-       struct work_struct work;
-       struct kfifo notif_fifo;
-       spinlock_t lock;
-       bool querying_devices;
-};
-
-struct dj_device {
-       struct hid_device *hdev;
-       struct dj_receiver_dev *dj_receiver_dev;
-       u32 reports_supported;
-       u8 device_index;
-};
-
-/**
- * is_dj_device - know if the given dj_device is not the receiver.
- * @dj_dev: the dj device to test
- *
- * This macro tests if a struct dj_device pointer is a device created
- * by the bus enumarator.
- */
-#define is_dj_device(dj_dev) \
-       (&(dj_dev)->dj_receiver_dev->hdev->dev == (dj_dev)->hdev->dev.parent)
-
-#endif
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
new file mode 100644 (file)
index 0000000..2f420c0
--- /dev/null
@@ -0,0 +1,1241 @@
+/*
+ *  HIDPP protocol for Logitech Unifying receivers
+ *
+ *  Copyright (c) 2011 Logitech (c)
+ *  Copyright (c) 2012-2013 Google (c)
+ *  Copyright (c) 2013-2014 Red Hat Inc.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/kfifo.h>
+#include <linux/input/mt.h>
+#include <asm/unaligned.h>
+#include "hid-ids.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
+MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>");
+
+#define REPORT_ID_HIDPP_SHORT                  0x10
+#define REPORT_ID_HIDPP_LONG                   0x11
+
+#define HIDPP_REPORT_SHORT_LENGTH              7
+#define HIDPP_REPORT_LONG_LENGTH               20
+
+#define HIDPP_QUIRK_CLASS_WTP                  BIT(0)
+
+/* bits 1..20 are reserved for classes */
+#define HIDPP_QUIRK_DELAYED_INIT               BIT(21)
+#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS       BIT(22)
+#define HIDPP_QUIRK_MULTI_INPUT                        BIT(23)
+
+/*
+ * There are two hidpp protocols in use, the first version hidpp10 is known
+ * as register access protocol or RAP, the second version hidpp20 is known as
+ * feature access protocol or FAP
+ *
+ * Most older devices (including the Unifying usb receiver) use the RAP protocol
+ * where as most newer devices use the FAP protocol. Both protocols are
+ * compatible with the underlying transport, which could be usb, Unifiying, or
+ * bluetooth. The message lengths are defined by the hid vendor specific report
+ * descriptor for the HIDPP_SHORT report type (total message lenth 7 bytes) and
+ * the HIDPP_LONG report type (total message length 20 bytes)
+ *
+ * The RAP protocol uses both report types, whereas the FAP only uses HIDPP_LONG
+ * messages. The Unifying receiver itself responds to RAP messages (device index
+ * is 0xFF for the receiver), and all messages (short or long) with a device
+ * index between 1 and 6 are passed untouched to the corresponding paired
+ * Unifying device.
+ *
+ * The paired device can be RAP or FAP, it will receive the message untouched
+ * from the Unifiying receiver.
+ */
+
+struct fap {
+       u8 feature_index;
+       u8 funcindex_clientid;
+       u8 params[HIDPP_REPORT_LONG_LENGTH - 4U];
+};
+
+struct rap {
+       u8 sub_id;
+       u8 reg_address;
+       u8 params[HIDPP_REPORT_LONG_LENGTH - 4U];
+};
+
+struct hidpp_report {
+       u8 report_id;
+       u8 device_index;
+       union {
+               struct fap fap;
+               struct rap rap;
+               u8 rawbytes[sizeof(struct fap)];
+       };
+} __packed;
+
+struct hidpp_device {
+       struct hid_device *hid_dev;
+       struct mutex send_mutex;
+       void *send_receive_buf;
+       wait_queue_head_t wait;
+       bool answer_available;
+       u8 protocol_major;
+       u8 protocol_minor;
+
+       void *private_data;
+
+       struct work_struct work;
+       struct kfifo delayed_work_fifo;
+       atomic_t connected;
+       struct input_dev *delayed_input;
+
+       unsigned long quirks;
+};
+
+
+#define HIDPP_ERROR                            0x8f
+#define HIDPP_ERROR_SUCCESS                    0x00
+#define HIDPP_ERROR_INVALID_SUBID              0x01
+#define HIDPP_ERROR_INVALID_ADRESS             0x02
+#define HIDPP_ERROR_INVALID_VALUE              0x03
+#define HIDPP_ERROR_CONNECT_FAIL               0x04
+#define HIDPP_ERROR_TOO_MANY_DEVICES           0x05
+#define HIDPP_ERROR_ALREADY_EXISTS             0x06
+#define HIDPP_ERROR_BUSY                       0x07
+#define HIDPP_ERROR_UNKNOWN_DEVICE             0x08
+#define HIDPP_ERROR_RESOURCE_ERROR             0x09
+#define HIDPP_ERROR_REQUEST_UNAVAILABLE                0x0a
+#define HIDPP_ERROR_INVALID_PARAM_VALUE                0x0b
+#define HIDPP_ERROR_WRONG_PIN_CODE             0x0c
+
+static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
+
+static int __hidpp_send_report(struct hid_device *hdev,
+                               struct hidpp_report *hidpp_report)
+{
+       int fields_count, ret;
+
+       switch (hidpp_report->report_id) {
+       case REPORT_ID_HIDPP_SHORT:
+               fields_count = HIDPP_REPORT_SHORT_LENGTH;
+               break;
+       case REPORT_ID_HIDPP_LONG:
+               fields_count = HIDPP_REPORT_LONG_LENGTH;
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       /*
+        * set the device_index as the receiver, it will be overwritten by
+        * hid_hw_request if needed
+        */
+       hidpp_report->device_index = 0xff;
+
+       ret = hid_hw_raw_request(hdev, hidpp_report->report_id,
+               (u8 *)hidpp_report, fields_count, HID_OUTPUT_REPORT,
+               HID_REQ_SET_REPORT);
+
+       return ret == fields_count ? 0 : -1;
+}
+
+/**
+ * hidpp_send_message_sync() returns 0 in case of success, and something else
+ * in case of a failure.
+ * - If ' something else' is positive, that means that an error has been raised
+ *   by the protocol itself.
+ * - If ' something else' is negative, that means that we had a classic error
+ *   (-ENOMEM, -EPIPE, etc...)
+ */
+static int hidpp_send_message_sync(struct hidpp_device *hidpp,
+       struct hidpp_report *message,
+       struct hidpp_report *response)
+{
+       int ret;
+
+       mutex_lock(&hidpp->send_mutex);
+
+       hidpp->send_receive_buf = response;
+       hidpp->answer_available = false;
+
+       /*
+        * So that we can later validate the answer when it arrives
+        * in hidpp_raw_event
+        */
+       *response = *message;
+
+       ret = __hidpp_send_report(hidpp->hid_dev, message);
+
+       if (ret) {
+               dbg_hid("__hidpp_send_report returned err: %d\n", ret);
+               memset(response, 0, sizeof(struct hidpp_report));
+               goto exit;
+       }
+
+       if (!wait_event_timeout(hidpp->wait, hidpp->answer_available,
+                               5*HZ)) {
+               dbg_hid("%s:timeout waiting for response\n", __func__);
+               memset(response, 0, sizeof(struct hidpp_report));
+               ret = -ETIMEDOUT;
+       }
+
+       if (response->report_id == REPORT_ID_HIDPP_SHORT &&
+           response->fap.feature_index == HIDPP_ERROR) {
+               ret = response->fap.params[1];
+               dbg_hid("__hidpp_send_report got hidpp error %02X\n", ret);
+               goto exit;
+       }
+
+exit:
+       mutex_unlock(&hidpp->send_mutex);
+       return ret;
+
+}
+
+static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp,
+       u8 feat_index, u8 funcindex_clientid, u8 *params, int param_count,
+       struct hidpp_report *response)
+{
+       struct hidpp_report *message;
+       int ret;
+
+       if (param_count > sizeof(message->fap.params))
+               return -EINVAL;
+
+       message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
+       if (!message)
+               return -ENOMEM;
+       message->report_id = REPORT_ID_HIDPP_LONG;
+       message->fap.feature_index = feat_index;
+       message->fap.funcindex_clientid = funcindex_clientid;
+       memcpy(&message->fap.params, params, param_count);
+
+       ret = hidpp_send_message_sync(hidpp, message, response);
+       kfree(message);
+       return ret;
+}
+
+static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
+       u8 report_id, u8 sub_id, u8 reg_address, u8 *params, int param_count,
+       struct hidpp_report *response)
+{
+       struct hidpp_report *message;
+       int ret;
+
+       if ((report_id != REPORT_ID_HIDPP_SHORT) &&
+           (report_id != REPORT_ID_HIDPP_LONG))
+               return -EINVAL;
+
+       if (param_count > sizeof(message->rap.params))
+               return -EINVAL;
+
+       message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
+       if (!message)
+               return -ENOMEM;
+       message->report_id = report_id;
+       message->rap.sub_id = sub_id;
+       message->rap.reg_address = reg_address;
+       memcpy(&message->rap.params, params, param_count);
+
+       ret = hidpp_send_message_sync(hidpp_dev, message, response);
+       kfree(message);
+       return ret;
+}
+
+static void delayed_work_cb(struct work_struct *work)
+{
+       struct hidpp_device *hidpp = container_of(work, struct hidpp_device,
+                                                       work);
+       hidpp_connect_event(hidpp);
+}
+
+static inline bool hidpp_match_answer(struct hidpp_report *question,
+               struct hidpp_report *answer)
+{
+       return (answer->fap.feature_index == question->fap.feature_index) &&
+          (answer->fap.funcindex_clientid == question->fap.funcindex_clientid);
+}
+
+static inline bool hidpp_match_error(struct hidpp_report *question,
+               struct hidpp_report *answer)
+{
+       return (answer->fap.feature_index == HIDPP_ERROR) &&
+           (answer->fap.funcindex_clientid == question->fap.feature_index) &&
+           (answer->fap.params[0] == question->fap.funcindex_clientid);
+}
+
+static inline bool hidpp_report_is_connect_event(struct hidpp_report *report)
+{
+       return (report->report_id == REPORT_ID_HIDPP_SHORT) &&
+               (report->rap.sub_id == 0x41);
+}
+
+/* -------------------------------------------------------------------------- */
+/* HIDP++ 1.0 commands                                                        */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_SET_REGISTER                             0x80
+#define HIDPP_GET_REGISTER                             0x81
+#define HIDPP_SET_LONG_REGISTER                                0x82
+#define HIDPP_GET_LONG_REGISTER                                0x83
+
+#define HIDPP_REG_PAIRING_INFORMATION                  0xB5
+#define DEVICE_NAME                                    0x40
+
+static char *hidpp_get_unifying_name(struct hidpp_device *hidpp_dev)
+{
+       struct hidpp_report response;
+       int ret;
+       /* hid-logitech-dj is in charge of setting the right device index */
+       u8 params[1] = { DEVICE_NAME };
+       char *name;
+       int len;
+
+       ret = hidpp_send_rap_command_sync(hidpp_dev,
+                                       REPORT_ID_HIDPP_SHORT,
+                                       HIDPP_GET_LONG_REGISTER,
+                                       HIDPP_REG_PAIRING_INFORMATION,
+                                       params, 1, &response);
+       if (ret)
+               return NULL;
+
+       len = response.rap.params[1];
+
+       if (2 + len > sizeof(response.rap.params))
+               return NULL;
+
+       name = kzalloc(len + 1, GFP_KERNEL);
+       if (!name)
+               return NULL;
+
+       memcpy(name, &response.rap.params[2], len);
+       return name;
+}
+
+/* -------------------------------------------------------------------------- */
+/* 0x0000: Root                                                               */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_ROOT                                        0x0000
+#define HIDPP_PAGE_ROOT_IDX                            0x00
+
+#define CMD_ROOT_GET_FEATURE                           0x01
+#define CMD_ROOT_GET_PROTOCOL_VERSION                  0x11
+
+static int hidpp_root_get_feature(struct hidpp_device *hidpp, u16 feature,
+       u8 *feature_index, u8 *feature_type)
+{
+       struct hidpp_report response;
+       int ret;
+       u8 params[2] = { feature >> 8, feature & 0x00FF };
+
+       ret = hidpp_send_fap_command_sync(hidpp,
+                       HIDPP_PAGE_ROOT_IDX,
+                       CMD_ROOT_GET_FEATURE,
+                       params, 2, &response);
+       if (ret)
+               return ret;
+
+       *feature_index = response.fap.params[0];
+       *feature_type = response.fap.params[1];
+
+       return ret;
+}
+
+static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp)
+{
+       struct hidpp_report response;
+       int ret;
+
+       ret = hidpp_send_fap_command_sync(hidpp,
+                       HIDPP_PAGE_ROOT_IDX,
+                       CMD_ROOT_GET_PROTOCOL_VERSION,
+                       NULL, 0, &response);
+
+       if (ret == HIDPP_ERROR_INVALID_SUBID) {
+               hidpp->protocol_major = 1;
+               hidpp->protocol_minor = 0;
+               return 0;
+       }
+
+       /* the device might not be connected */
+       if (ret == HIDPP_ERROR_RESOURCE_ERROR)
+               return -EIO;
+
+       if (ret > 0) {
+               hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+                       __func__, ret);
+               return -EPROTO;
+       }
+       if (ret)
+               return ret;
+
+       hidpp->protocol_major = response.fap.params[0];
+       hidpp->protocol_minor = response.fap.params[1];
+
+       return ret;
+}
+
+static bool hidpp_is_connected(struct hidpp_device *hidpp)
+{
+       int ret;
+
+       ret = hidpp_root_get_protocol_version(hidpp);
+       if (!ret)
+               hid_dbg(hidpp->hid_dev, "HID++ %u.%u device connected.\n",
+                       hidpp->protocol_major, hidpp->protocol_minor);
+       return ret == 0;
+}
+
+/* -------------------------------------------------------------------------- */
+/* 0x0005: GetDeviceNameType                                                  */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_GET_DEVICE_NAME_TYPE                        0x0005
+
+#define CMD_GET_DEVICE_NAME_TYPE_GET_COUNT             0x01
+#define CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME       0x11
+#define CMD_GET_DEVICE_NAME_TYPE_GET_TYPE              0x21
+
+static int hidpp_devicenametype_get_count(struct hidpp_device *hidpp,
+       u8 feature_index, u8 *nameLength)
+{
+       struct hidpp_report response;
+       int ret;
+
+       ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+               CMD_GET_DEVICE_NAME_TYPE_GET_COUNT, NULL, 0, &response);
+
+       if (ret > 0) {
+               hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+                       __func__, ret);
+               return -EPROTO;
+       }
+       if (ret)
+               return ret;
+
+       *nameLength = response.fap.params[0];
+
+       return ret;
+}
+
+static int hidpp_devicenametype_get_device_name(struct hidpp_device *hidpp,
+       u8 feature_index, u8 char_index, char *device_name, int len_buf)
+{
+       struct hidpp_report response;
+       int ret, i;
+       int count;
+
+       ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+               CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME, &char_index, 1,
+               &response);
+
+       if (ret > 0) {
+               hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+                       __func__, ret);
+               return -EPROTO;
+       }
+       if (ret)
+               return ret;
+
+       if (response.report_id == REPORT_ID_HIDPP_LONG)
+               count = HIDPP_REPORT_LONG_LENGTH - 4;
+       else
+               count = HIDPP_REPORT_SHORT_LENGTH - 4;
+
+       if (len_buf < count)
+               count = len_buf;
+
+       for (i = 0; i < count; i++)
+               device_name[i] = response.fap.params[i];
+
+       return count;
+}
+
+static char *hidpp_get_device_name(struct hidpp_device *hidpp)
+{
+       u8 feature_type;
+       u8 feature_index;
+       u8 __name_length;
+       char *name;
+       unsigned index = 0;
+       int ret;
+
+       ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_GET_DEVICE_NAME_TYPE,
+               &feature_index, &feature_type);
+       if (ret)
+               return NULL;
+
+       ret = hidpp_devicenametype_get_count(hidpp, feature_index,
+               &__name_length);
+       if (ret)
+               return NULL;
+
+       name = kzalloc(__name_length + 1, GFP_KERNEL);
+       if (!name)
+               return NULL;
+
+       while (index < __name_length) {
+               ret = hidpp_devicenametype_get_device_name(hidpp,
+                       feature_index, index, name + index,
+                       __name_length - index);
+               if (ret <= 0) {
+                       kfree(name);
+                       return NULL;
+               }
+               index += ret;
+       }
+
+       return name;
+}
+
+/* -------------------------------------------------------------------------- */
+/* 0x6100: TouchPadRawXY                                                      */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_TOUCHPAD_RAW_XY                     0x6100
+
+#define CMD_TOUCHPAD_GET_RAW_INFO                      0x01
+#define CMD_TOUCHPAD_SET_RAW_REPORT_STATE              0x21
+
+#define EVENT_TOUCHPAD_RAW_XY                          0x00
+
+#define TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT              0x01
+#define TOUCHPAD_RAW_XY_ORIGIN_UPPER_LEFT              0x03
+
+struct hidpp_touchpad_raw_info {
+       u16 x_size;
+       u16 y_size;
+       u8 z_range;
+       u8 area_range;
+       u8 timestamp_unit;
+       u8 maxcontacts;
+       u8 origin;
+       u16 res;
+};
+
+struct hidpp_touchpad_raw_xy_finger {
+       u8 contact_type;
+       u8 contact_status;
+       u16 x;
+       u16 y;
+       u8 z;
+       u8 area;
+       u8 finger_id;
+};
+
+struct hidpp_touchpad_raw_xy {
+       u16 timestamp;
+       struct hidpp_touchpad_raw_xy_finger fingers[2];
+       u8 spurious_flag;
+       u8 end_of_frame;
+       u8 finger_count;
+       u8 button;
+};
+
+static int hidpp_touchpad_get_raw_info(struct hidpp_device *hidpp,
+       u8 feature_index, struct hidpp_touchpad_raw_info *raw_info)
+{
+       struct hidpp_report response;
+       int ret;
+       u8 *params = (u8 *)response.fap.params;
+
+       ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+               CMD_TOUCHPAD_GET_RAW_INFO, NULL, 0, &response);
+
+       if (ret > 0) {
+               hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+                       __func__, ret);
+               return -EPROTO;
+       }
+       if (ret)
+               return ret;
+
+       raw_info->x_size = get_unaligned_be16(&params[0]);
+       raw_info->y_size = get_unaligned_be16(&params[2]);
+       raw_info->z_range = params[4];
+       raw_info->area_range = params[5];
+       raw_info->maxcontacts = params[7];
+       raw_info->origin = params[8];
+       /* res is given in unit per inch */
+       raw_info->res = get_unaligned_be16(&params[13]) * 2 / 51;
+
+       return ret;
+}
+
+static int hidpp_touchpad_set_raw_report_state(struct hidpp_device *hidpp_dev,
+               u8 feature_index, bool send_raw_reports,
+               bool sensor_enhanced_settings)
+{
+       struct hidpp_report response;
+
+       /*
+        * Params:
+        *   bit 0 - enable raw
+        *   bit 1 - 16bit Z, no area
+        *   bit 2 - enhanced sensitivity
+        *   bit 3 - width, height (4 bits each) instead of area
+        *   bit 4 - send raw + gestures (degrades smoothness)
+        *   remaining bits - reserved
+        */
+       u8 params = send_raw_reports | (sensor_enhanced_settings << 2);
+
+       return hidpp_send_fap_command_sync(hidpp_dev, feature_index,
+               CMD_TOUCHPAD_SET_RAW_REPORT_STATE, &params, 1, &response);
+}
+
+static void hidpp_touchpad_touch_event(u8 *data,
+       struct hidpp_touchpad_raw_xy_finger *finger)
+{
+       u8 x_m = data[0] << 2;
+       u8 y_m = data[2] << 2;
+
+       finger->x = x_m << 6 | data[1];
+       finger->y = y_m << 6 | data[3];
+
+       finger->contact_type = data[0] >> 6;
+       finger->contact_status = data[2] >> 6;
+
+       finger->z = data[4];
+       finger->area = data[5];
+       finger->finger_id = data[6] >> 4;
+}
+
+static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
+               u8 *data, struct hidpp_touchpad_raw_xy *raw_xy)
+{
+       memset(raw_xy, 0, sizeof(struct hidpp_touchpad_raw_xy));
+       raw_xy->end_of_frame = data[8] & 0x01;
+       raw_xy->spurious_flag = (data[8] >> 1) & 0x01;
+       raw_xy->finger_count = data[15] & 0x0f;
+       raw_xy->button = (data[8] >> 2) & 0x01;
+
+       if (raw_xy->finger_count) {
+               hidpp_touchpad_touch_event(&data[2], &raw_xy->fingers[0]);
+               hidpp_touchpad_touch_event(&data[9], &raw_xy->fingers[1]);
+       }
+}
+
+/* ************************************************************************** */
+/*                                                                            */
+/* Device Support                                                             */
+/*                                                                            */
+/* ************************************************************************** */
+
+/* -------------------------------------------------------------------------- */
+/* Touchpad HID++ devices                                                     */
+/* -------------------------------------------------------------------------- */
+
+#define WTP_MANUAL_RESOLUTION                          39
+
+struct wtp_data {
+       struct input_dev *input;
+       u16 x_size, y_size;
+       u8 finger_count;
+       u8 mt_feature_index;
+       u8 button_feature_index;
+       u8 maxcontacts;
+       bool flip_y;
+       unsigned int resolution;
+};
+
+static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+
+       if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) &&
+           (field->application == HID_GD_KEYBOARD))
+               return 0;
+
+       return -1;
+}
+
+static void wtp_populate_input(struct hidpp_device *hidpp,
+               struct input_dev *input_dev, bool origin_is_hid_core)
+{
+       struct wtp_data *wd = hidpp->private_data;
+
+       if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) && origin_is_hid_core)
+               /* this is the generic hid-input call */
+               return;
+
+       __set_bit(EV_ABS, input_dev->evbit);
+       __set_bit(EV_KEY, input_dev->evbit);
+       __clear_bit(EV_REL, input_dev->evbit);
+       __clear_bit(EV_LED, input_dev->evbit);
+
+       input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, wd->x_size, 0, 0);
+       input_abs_set_res(input_dev, ABS_MT_POSITION_X, wd->resolution);
+       input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, wd->y_size, 0, 0);
+       input_abs_set_res(input_dev, ABS_MT_POSITION_Y, wd->resolution);
+
+       /* Max pressure is not given by the devices, pick one */
+       input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 50, 0, 0);
+
+       input_set_capability(input_dev, EV_KEY, BTN_LEFT);
+
+       if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS)
+               input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
+       else
+               __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+
+       input_mt_init_slots(input_dev, wd->maxcontacts, INPUT_MT_POINTER |
+               INPUT_MT_DROP_UNUSED);
+
+       wd->input = input_dev;
+}
+
+static void wtp_touch_event(struct wtp_data *wd,
+       struct hidpp_touchpad_raw_xy_finger *touch_report)
+{
+       int slot;
+
+       if (!touch_report->finger_id || touch_report->contact_type)
+               /* no actual data */
+               return;
+
+       slot = input_mt_get_slot_by_key(wd->input, touch_report->finger_id);
+
+       input_mt_slot(wd->input, slot);
+       input_mt_report_slot_state(wd->input, MT_TOOL_FINGER,
+                                       touch_report->contact_status);
+       if (touch_report->contact_status) {
+               input_event(wd->input, EV_ABS, ABS_MT_POSITION_X,
+                               touch_report->x);
+               input_event(wd->input, EV_ABS, ABS_MT_POSITION_Y,
+                               wd->flip_y ? wd->y_size - touch_report->y :
+                                            touch_report->y);
+               input_event(wd->input, EV_ABS, ABS_MT_PRESSURE,
+                               touch_report->area);
+       }
+}
+
+static void wtp_send_raw_xy_event(struct hidpp_device *hidpp,
+               struct hidpp_touchpad_raw_xy *raw)
+{
+       struct wtp_data *wd = hidpp->private_data;
+       int i;
+
+       for (i = 0; i < 2; i++)
+               wtp_touch_event(wd, &(raw->fingers[i]));
+
+       if (raw->end_of_frame &&
+           !(hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS))
+               input_event(wd->input, EV_KEY, BTN_LEFT, raw->button);
+
+       if (raw->end_of_frame || raw->finger_count <= 2) {
+               input_mt_sync_frame(wd->input);
+               input_sync(wd->input);
+       }
+}
+
+static int wtp_mouse_raw_xy_event(struct hidpp_device *hidpp, u8 *data)
+{
+       struct wtp_data *wd = hidpp->private_data;
+       u8 c1_area = ((data[7] & 0xf) * (data[7] & 0xf) +
+                     (data[7] >> 4) * (data[7] >> 4)) / 2;
+       u8 c2_area = ((data[13] & 0xf) * (data[13] & 0xf) +
+                     (data[13] >> 4) * (data[13] >> 4)) / 2;
+       struct hidpp_touchpad_raw_xy raw = {
+               .timestamp = data[1],
+               .fingers = {
+                       {
+                               .contact_type = 0,
+                               .contact_status = !!data[7],
+                               .x = get_unaligned_le16(&data[3]),
+                               .y = get_unaligned_le16(&data[5]),
+                               .z = c1_area,
+                               .area = c1_area,
+                               .finger_id = data[2],
+                       }, {
+                               .contact_type = 0,
+                               .contact_status = !!data[13],
+                               .x = get_unaligned_le16(&data[9]),
+                               .y = get_unaligned_le16(&data[11]),
+                               .z = c2_area,
+                               .area = c2_area,
+                               .finger_id = data[8],
+                       }
+               },
+               .finger_count = wd->maxcontacts,
+               .spurious_flag = 0,
+               .end_of_frame = (data[0] >> 7) == 0,
+               .button = data[0] & 0x01,
+       };
+
+       wtp_send_raw_xy_event(hidpp, &raw);
+
+       return 1;
+}
+
+static int wtp_raw_event(struct hid_device *hdev, u8 *data, int size)
+{
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+       struct wtp_data *wd = hidpp->private_data;
+       struct hidpp_report *report = (struct hidpp_report *)data;
+       struct hidpp_touchpad_raw_xy raw;
+
+       if (!wd || !wd->input)
+               return 1;
+
+       switch (data[0]) {
+       case 0x02:
+               if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS) {
+                       input_event(wd->input, EV_KEY, BTN_LEFT,
+                                       !!(data[1] & 0x01));
+                       input_event(wd->input, EV_KEY, BTN_RIGHT,
+                                       !!(data[1] & 0x02));
+                       input_sync(wd->input);
+               } else {
+                       if (size < 21)
+                               return 1;
+                       return wtp_mouse_raw_xy_event(hidpp, &data[7]);
+               }
+       case REPORT_ID_HIDPP_LONG:
+               if ((report->fap.feature_index != wd->mt_feature_index) ||
+                   (report->fap.funcindex_clientid != EVENT_TOUCHPAD_RAW_XY))
+                       return 1;
+               hidpp_touchpad_raw_xy_event(hidpp, data + 4, &raw);
+
+               wtp_send_raw_xy_event(hidpp, &raw);
+               return 0;
+       }
+
+       return 0;
+}
+
+static int wtp_get_config(struct hidpp_device *hidpp)
+{
+       struct wtp_data *wd = hidpp->private_data;
+       struct hidpp_touchpad_raw_info raw_info = {0};
+       u8 feature_type;
+       int ret;
+
+       ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_TOUCHPAD_RAW_XY,
+               &wd->mt_feature_index, &feature_type);
+       if (ret)
+               /* means that the device is not powered up */
+               return ret;
+
+       ret = hidpp_touchpad_get_raw_info(hidpp, wd->mt_feature_index,
+               &raw_info);
+       if (ret)
+               return ret;
+
+       wd->x_size = raw_info.x_size;
+       wd->y_size = raw_info.y_size;
+       wd->maxcontacts = raw_info.maxcontacts;
+       wd->flip_y = raw_info.origin == TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT;
+       wd->resolution = raw_info.res;
+       if (!wd->resolution)
+               wd->resolution = WTP_MANUAL_RESOLUTION;
+
+       return 0;
+}
+
+static int wtp_allocate(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+       struct wtp_data *wd;
+
+       wd = devm_kzalloc(&hdev->dev, sizeof(struct wtp_data),
+                       GFP_KERNEL);
+       if (!wd)
+               return -ENOMEM;
+
+       hidpp->private_data = wd;
+
+       return 0;
+};
+
+static void wtp_connect(struct hid_device *hdev, bool connected)
+{
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+       struct wtp_data *wd = hidpp->private_data;
+       int ret;
+
+       if (!connected)
+               return;
+
+       if (!wd->x_size) {
+               ret = wtp_get_config(hidpp);
+               if (ret) {
+                       hid_err(hdev, "Can not get wtp config: %d\n", ret);
+                       return;
+               }
+       }
+
+       hidpp_touchpad_set_raw_report_state(hidpp, wd->mt_feature_index,
+                       true, true);
+}
+
+/* -------------------------------------------------------------------------- */
+/* Generic HID++ devices                                                      */
+/* -------------------------------------------------------------------------- */
+
+static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+
+       if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
+               return wtp_input_mapping(hdev, hi, field, usage, bit, max);
+
+       return 0;
+}
+
+static void hidpp_populate_input(struct hidpp_device *hidpp,
+               struct input_dev *input, bool origin_is_hid_core)
+{
+       if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
+               wtp_populate_input(hidpp, input, origin_is_hid_core);
+}
+
+static void hidpp_input_configured(struct hid_device *hdev,
+                               struct hid_input *hidinput)
+{
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+       struct input_dev *input = hidinput->input;
+
+       hidpp_populate_input(hidpp, input, true);
+}
+
+static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
+               int size)
+{
+       struct hidpp_report *question = hidpp->send_receive_buf;
+       struct hidpp_report *answer = hidpp->send_receive_buf;
+       struct hidpp_report *report = (struct hidpp_report *)data;
+
+       /*
+        * If the mutex is locked then we have a pending answer from a
+        * previoulsly sent command
+        */
+       if (unlikely(mutex_is_locked(&hidpp->send_mutex))) {
+               /*
+                * Check for a correct hidpp20 answer or the corresponding
+                * error
+                */
+               if (hidpp_match_answer(question, report) ||
+                               hidpp_match_error(question, report)) {
+                       *answer = *report;
+                       hidpp->answer_available = true;
+                       wake_up(&hidpp->wait);
+                       /*
+                        * This was an answer to a command that this driver sent
+                        * We return 1 to hid-core to avoid forwarding the
+                        * command upstream as it has been treated by the driver
+                        */
+
+                       return 1;
+               }
+       }
+
+       if (unlikely(hidpp_report_is_connect_event(report))) {
+               atomic_set(&hidpp->connected,
+                               !(report->rap.params[0] & (1 << 6)));
+               if ((hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) &&
+                   (schedule_work(&hidpp->work) == 0))
+                       dbg_hid("%s: connect event already queued\n", __func__);
+               return 1;
+       }
+
+       if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
+               return wtp_raw_event(hidpp->hid_dev, data, size);
+
+       return 0;
+}
+
+static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
+               u8 *data, int size)
+{
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+
+       switch (data[0]) {
+       case REPORT_ID_HIDPP_LONG:
+               if (size != HIDPP_REPORT_LONG_LENGTH) {
+                       hid_err(hdev, "received hid++ report of bad size (%d)",
+                               size);
+                       return 1;
+               }
+               return hidpp_raw_hidpp_event(hidpp, data, size);
+       case REPORT_ID_HIDPP_SHORT:
+               if (size != HIDPP_REPORT_SHORT_LENGTH) {
+                       hid_err(hdev, "received hid++ report of bad size (%d)",
+                               size);
+                       return 1;
+               }
+               return hidpp_raw_hidpp_event(hidpp, data, size);
+       }
+
+       if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
+               return wtp_raw_event(hdev, data, size);
+
+       return 0;
+}
+
+static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying)
+{
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+       char *name;
+
+       if (use_unifying)
+               /*
+                * the device is connected through an Unifying receiver, and
+                * might not be already connected.
+                * Ask the receiver for its name.
+                */
+               name = hidpp_get_unifying_name(hidpp);
+       else
+               name = hidpp_get_device_name(hidpp);
+
+       if (!name)
+               hid_err(hdev, "unable to retrieve the name of the device");
+       else
+               snprintf(hdev->name, sizeof(hdev->name), "%s", name);
+
+       kfree(name);
+}
+
+static int hidpp_input_open(struct input_dev *dev)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+
+       return hid_hw_open(hid);
+}
+
+static void hidpp_input_close(struct input_dev *dev)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+
+       hid_hw_close(hid);
+}
+
+static struct input_dev *hidpp_allocate_input(struct hid_device *hdev)
+{
+       struct input_dev *input_dev = devm_input_allocate_device(&hdev->dev);
+
+       if (!input_dev)
+               return NULL;
+
+       input_set_drvdata(input_dev, hdev);
+       input_dev->open = hidpp_input_open;
+       input_dev->close = hidpp_input_close;
+
+       input_dev->name = hdev->name;
+       input_dev->phys = hdev->phys;
+       input_dev->uniq = hdev->uniq;
+       input_dev->id.bustype = hdev->bus;
+       input_dev->id.vendor  = hdev->vendor;
+       input_dev->id.product = hdev->product;
+       input_dev->id.version = hdev->version;
+       input_dev->dev.parent = &hdev->dev;
+
+       return input_dev;
+}
+
+static void hidpp_connect_event(struct hidpp_device *hidpp)
+{
+       struct hid_device *hdev = hidpp->hid_dev;
+       int ret = 0;
+       bool connected = atomic_read(&hidpp->connected);
+       struct input_dev *input;
+       char *name, *devm_name;
+
+       if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
+               wtp_connect(hdev, connected);
+
+       if (!connected || hidpp->delayed_input)
+               return;
+
+       if (!hidpp->protocol_major) {
+               ret = !hidpp_is_connected(hidpp);
+               if (ret) {
+                       hid_err(hdev, "Can not get the protocol version.\n");
+                       return;
+               }
+       }
+
+       /* the device is already connected, we can ask for its name and
+        * protocol */
+       hid_info(hdev, "HID++ %u.%u device connected.\n",
+                hidpp->protocol_major, hidpp->protocol_minor);
+
+       input = hidpp_allocate_input(hdev);
+       if (!input) {
+               hid_err(hdev, "cannot allocate new input device: %d\n", ret);
+               return;
+       }
+
+       name = hidpp_get_device_name(hidpp);
+       if (!name) {
+               hid_err(hdev, "unable to retrieve the name of the device");
+       } else {
+               devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s", name);
+               if (devm_name)
+                       input->name = devm_name;
+               kfree(name);
+       }
+
+       hidpp_populate_input(hidpp, input, false);
+
+       ret = input_register_device(input);
+       if (ret)
+               input_free_device(input);
+
+       hidpp->delayed_input = input;
+}
+
+static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       struct hidpp_device *hidpp;
+       int ret;
+       bool connected;
+       unsigned int connect_mask = HID_CONNECT_DEFAULT;
+
+       hidpp = devm_kzalloc(&hdev->dev, sizeof(struct hidpp_device),
+                       GFP_KERNEL);
+       if (!hidpp)
+               return -ENOMEM;
+
+       hidpp->hid_dev = hdev;
+       hid_set_drvdata(hdev, hidpp);
+
+       hidpp->quirks = id->driver_data;
+
+       if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
+               ret = wtp_allocate(hdev, id);
+               if (ret)
+                       goto wtp_allocate_fail;
+       }
+
+       INIT_WORK(&hidpp->work, delayed_work_cb);
+       mutex_init(&hidpp->send_mutex);
+       init_waitqueue_head(&hidpp->wait);
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "%s:parse failed\n", __func__);
+               goto hid_parse_fail;
+       }
+
+       /* Allow incoming packets */
+       hid_device_io_start(hdev);
+
+       connected = hidpp_is_connected(hidpp);
+       if (id->group != HID_GROUP_LOGITECH_DJ_DEVICE) {
+               if (!connected) {
+                       hid_err(hdev, "Device not connected");
+                       hid_device_io_stop(hdev);
+                       goto hid_parse_fail;
+               }
+
+               hid_info(hdev, "HID++ %u.%u device connected.\n",
+                        hidpp->protocol_major, hidpp->protocol_minor);
+       }
+
+       hidpp_overwrite_name(hdev, id->group == HID_GROUP_LOGITECH_DJ_DEVICE);
+       atomic_set(&hidpp->connected, connected);
+
+       if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
+               ret = wtp_get_config(hidpp);
+               if (ret)
+                       goto hid_parse_fail;
+       }
+
+       /* Block incoming packets */
+       hid_device_io_stop(hdev);
+
+       if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
+               connect_mask &= ~HID_CONNECT_HIDINPUT;
+
+       /* Re-enable hidinput for multi-input devices */
+       if (hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT)
+               connect_mask |= HID_CONNECT_HIDINPUT;
+
+       ret = hid_hw_start(hdev, connect_mask);
+       if (ret) {
+               hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
+               goto hid_hw_start_fail;
+       }
+
+       if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) {
+               /* Allow incoming packets */
+               hid_device_io_start(hdev);
+
+               hidpp_connect_event(hidpp);
+       }
+
+       return ret;
+
+hid_hw_start_fail:
+hid_parse_fail:
+       cancel_work_sync(&hidpp->work);
+       mutex_destroy(&hidpp->send_mutex);
+wtp_allocate_fail:
+       hid_set_drvdata(hdev, NULL);
+       return ret;
+}
+
+static void hidpp_remove(struct hid_device *hdev)
+{
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+
+       cancel_work_sync(&hidpp->work);
+       mutex_destroy(&hidpp->send_mutex);
+       hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id hidpp_devices[] = {
+       { /* wireless touchpad */
+         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+               USB_VENDOR_ID_LOGITECH, 0x4011),
+         .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT |
+                        HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS },
+       { /* wireless touchpad T650 */
+         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+               USB_VENDOR_ID_LOGITECH, 0x4101),
+         .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT },
+       { /* wireless touchpad T651 */
+         HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
+               USB_DEVICE_ID_LOGITECH_T651),
+         .driver_data = HIDPP_QUIRK_CLASS_WTP },
+       { /* Keyboard TK820 */
+         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+               USB_VENDOR_ID_LOGITECH, 0x4102),
+         .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_MULTI_INPUT |
+                        HIDPP_QUIRK_CLASS_WTP },
+
+       { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+               USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
+       {}
+};
+
+MODULE_DEVICE_TABLE(hid, hidpp_devices);
+
+static struct hid_driver hidpp_driver = {
+       .name = "logitech-hidpp-device",
+       .id_table = hidpp_devices,
+       .probe = hidpp_probe,
+       .remove = hidpp_remove,
+       .raw_event = hidpp_raw_event,
+       .input_configured = hidpp_input_configured,
+       .input_mapping = hidpp_input_mapping,
+};
+
+module_hid_driver(hidpp_driver);
index 8ba17a9..cacda43 100644 (file)
@@ -274,6 +274,8 @@ static const struct hid_device_id ms_devices[] = {
                .driver_data = MS_NOGET },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500),
                .driver_data = MS_DUPLICATE_USAGES },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3),
+               .driver_data = MS_HIDINPUT },
 
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
                .driver_data = MS_PRESENTER },
index 51e25b9..f65e78b 100644 (file)
@@ -67,6 +67,7 @@ MODULE_LICENSE("GPL");
 #define MT_QUIRK_IGNORE_DUPLICATES     (1 << 10)
 #define MT_QUIRK_HOVERING              (1 << 11)
 #define MT_QUIRK_CONTACT_CNT_ACCURATE  (1 << 12)
+#define MT_QUIRK_FORCE_GET_FEATURE     (1 << 13)
 
 #define MT_INPUTMODE_TOUCHSCREEN       0x02
 #define MT_INPUTMODE_TOUCHPAD          0x03
@@ -150,6 +151,7 @@ static void mt_post_parse(struct mt_device *td);
 #define MT_CLS_FLATFROG                                0x0107
 #define MT_CLS_GENERALTOUCH_TWOFINGERS         0x0108
 #define MT_CLS_GENERALTOUCH_PWT_TENFINGERS     0x0109
+#define MT_CLS_VTL                             0x0110
 
 #define MT_DEFAULT_MAXCONTACT  10
 #define MT_MAX_MAXCONTACT      250
@@ -255,6 +257,11 @@ static struct mt_class mt_classes[] = {
                .sn_move = 2048,
                .maxcontacts = 40,
        },
+       { .name = MT_CLS_VTL,
+               .quirks = MT_QUIRK_ALWAYS_VALID |
+                       MT_QUIRK_CONTACT_CNT_ACCURATE |
+                       MT_QUIRK_FORCE_GET_FEATURE,
+       },
        { }
 };
 
@@ -809,6 +816,9 @@ static void mt_set_input_mode(struct hid_device *hdev)
        struct mt_device *td = hid_get_drvdata(hdev);
        struct hid_report *r;
        struct hid_report_enum *re;
+       struct mt_class *cls = &td->mtclass;
+       char *buf;
+       int report_len;
 
        if (td->inputmode < 0)
                return;
@@ -816,6 +826,18 @@ static void mt_set_input_mode(struct hid_device *hdev)
        re = &(hdev->report_enum[HID_FEATURE_REPORT]);
        r = re->report_id_hash[td->inputmode];
        if (r) {
+               if (cls->quirks & MT_QUIRK_FORCE_GET_FEATURE) {
+                       report_len = hid_report_len(r);
+                       buf = hid_alloc_report_buf(r, GFP_KERNEL);
+                       if (!buf) {
+                               hid_err(hdev, "failed to allocate buffer for report\n");
+                               return;
+                       }
+                       hid_hw_raw_request(hdev, r->id, buf, report_len,
+                                          HID_FEATURE_REPORT,
+                                          HID_REQ_GET_REPORT);
+                       kfree(buf);
+               }
                r->field[0]->value[td->inputmode_index] = td->inputmode_value;
                hid_hw_request(hdev, r, HID_REQ_SET_REPORT);
        }
@@ -1281,6 +1303,11 @@ static const struct hid_device_id mt_devices[] = {
                MT_USB_DEVICE(USB_VENDOR_ID_UNITEC,
                        USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) },
 
+       /* VTL panels */
+       { .driver_data = MT_CLS_VTL,
+               MT_USB_DEVICE(USB_VENDOR_ID_VTL,
+                       USB_DEVICE_ID_VTL_MULTITOUCH_FF3F) },
+
        /* Wistron panels */
        { .driver_data = MT_CLS_NSMU,
                MT_USB_DEVICE(USB_VENDOR_ID_WISTRON,
diff --git a/drivers/hid/hid-plantronics.c b/drivers/hid/hid-plantronics.c
new file mode 100644 (file)
index 0000000..2180e07
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  Plantronics USB HID Driver
+ *
+ *  Copyright (c) 2014 JD Cole <jd.cole@plantronics.com>
+ *  Copyright (c) 2014 Terry Junge <terry.junge@plantronics.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include "hid-ids.h"
+
+#include <linux/hid.h>
+#include <linux/module.h>
+
+static int plantronics_input_mapping(struct hid_device *hdev,
+                                    struct hid_input *hi,
+                                    struct hid_field *field,
+                                    struct hid_usage *usage,
+                                    unsigned long **bit, int *max)
+{
+       if (field->application == HID_CP_CONSUMERCONTROL
+           && (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
+               hid_dbg(hdev, "usage: %08x (appl: %08x) - defaulted\n",
+                        usage->hid, field->application);
+               return 0;
+       }
+
+       hid_dbg(hdev, "usage: %08x (appl: %08x) - ignored\n",
+               usage->hid, field->application);
+
+       return -1;
+}
+
+static const struct hid_device_id plantronics_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, plantronics_devices);
+
+static struct hid_driver plantronics_driver = {
+       .name = "plantronics",
+       .id_table = plantronics_devices,
+       .input_mapping = plantronics_input_mapping,
+};
+module_hid_driver(plantronics_driver);
+
+MODULE_AUTHOR("JD Cole <jd.cole@plantronics.com>");
+MODULE_AUTHOR("Terry Junge <terry.junge@plantronics.com>");
+MODULE_DESCRIPTION("Plantronics USB HID Driver");
+MODULE_LICENSE("GPL");
index 3cccff7..b51200f 100644 (file)
@@ -584,11 +584,15 @@ static int rmi_populate_f11(struct hid_device *hdev)
        bool has_query10 = false;
        bool has_query11;
        bool has_query12;
+       bool has_query27;
+       bool has_query28;
+       bool has_query36 = false;
        bool has_physical_props;
        bool has_gestures;
        bool has_rel;
+       bool has_data40 = false;
        unsigned x_size, y_size;
-       u16 query12_offset;
+       u16 query_offset;
 
        if (!data->f11.query_base_addr) {
                hid_err(hdev, "No 2D sensor found, giving up.\n");
@@ -604,6 +608,8 @@ static int rmi_populate_f11(struct hid_device *hdev)
        has_query9 = !!(buf[0] & BIT(3));
        has_query11 = !!(buf[0] & BIT(4));
        has_query12 = !!(buf[0] & BIT(5));
+       has_query27 = !!(buf[0] & BIT(6));
+       has_query28 = !!(buf[0] & BIT(7));
 
        /* query 1 to get the max number of fingers */
        ret = rmi_read(hdev, data->f11.query_base_addr + 1, buf);
@@ -626,43 +632,43 @@ static int rmi_populate_f11(struct hid_device *hdev)
        has_rel = !!(buf[0] & BIT(3));
        has_gestures = !!(buf[0] & BIT(5));
 
+       /*
+        * At least 4 queries are guaranteed to be present in F11
+        * +1 for query 5 which is present since absolute events are
+        * reported and +1 for query 12.
+        */
+       query_offset = 6;
+
+       if (has_rel)
+               ++query_offset; /* query 6 is present */
+
        if (has_gestures) {
                /* query 8 to find out if query 10 exists */
-               ret = rmi_read(hdev, data->f11.query_base_addr + 8, buf);
+               ret = rmi_read(hdev,
+                       data->f11.query_base_addr + query_offset + 1, buf);
                if (ret) {
                        hid_err(hdev, "can not read gesture information: %d.\n",
                                ret);
                        return ret;
                }
                has_query10 = !!(buf[0] & BIT(2));
-       }
 
-       /*
-        * At least 4 queries are guaranteed to be present in F11
-        * +1 for query 5 which is present since absolute events are
-        * reported and +1 for query 12.
-        */
-       query12_offset = 6;
-
-       if (has_rel)
-               ++query12_offset; /* query 6 is present */
-
-       if (has_gestures)
-               query12_offset += 2; /* query 7 and 8 are present */
+               query_offset += 2; /* query 7 and 8 are present */
+       }
 
        if (has_query9)
-               ++query12_offset;
+               ++query_offset;
 
        if (has_query10)
-               ++query12_offset;
+               ++query_offset;
 
        if (has_query11)
-               ++query12_offset;
+               ++query_offset;
 
        /* query 12 to know if the physical properties are reported */
        if (has_query12) {
                ret = rmi_read(hdev, data->f11.query_base_addr
-                               + query12_offset, buf);
+                               + query_offset, buf);
                if (ret) {
                        hid_err(hdev, "can not get query 12: %d.\n", ret);
                        return ret;
@@ -670,9 +676,10 @@ static int rmi_populate_f11(struct hid_device *hdev)
                has_physical_props = !!(buf[0] & BIT(5));
 
                if (has_physical_props) {
+                       query_offset += 1;
                        ret = rmi_read_block(hdev,
                                        data->f11.query_base_addr
-                                               + query12_offset + 1, buf, 4);
+                                               + query_offset, buf, 4);
                        if (ret) {
                                hid_err(hdev, "can not read query 15-18: %d.\n",
                                        ret);
@@ -687,9 +694,45 @@ static int rmi_populate_f11(struct hid_device *hdev)
 
                        hid_info(hdev, "%s: size in mm: %d x %d\n",
                                 __func__, data->x_size_mm, data->y_size_mm);
+
+                       /*
+                        * query 15 - 18 contain the size of the sensor
+                        * and query 19 - 26 contain bezel dimensions
+                        */
+                       query_offset += 12;
+               }
+       }
+
+       if (has_query27)
+               ++query_offset;
+
+       if (has_query28) {
+               ret = rmi_read(hdev, data->f11.query_base_addr
+                               + query_offset, buf);
+               if (ret) {
+                       hid_err(hdev, "can not get query 28: %d.\n", ret);
+                       return ret;
+               }
+
+               has_query36 = !!(buf[0] & BIT(6));
+       }
+
+       if (has_query36) {
+               query_offset += 2;
+               ret = rmi_read(hdev, data->f11.query_base_addr
+                               + query_offset, buf);
+               if (ret) {
+                       hid_err(hdev, "can not get query 36: %d.\n", ret);
+                       return ret;
                }
+
+               has_data40 = !!(buf[0] & BIT(5));
        }
 
+
+       if (has_data40)
+               data->f11.report_size += data->max_fingers * 2;
+
        /*
         * retrieve the ctrl registers
         * the ctrl register has a size of 20 but a fw bug split it into 16 + 4,
index 6101816..c292650 100644 (file)
@@ -46,6 +46,7 @@ static void kone_profile_activated(struct kone_device *kone, uint new_profile)
 static void kone_profile_report(struct kone_device *kone, uint new_profile)
 {
        struct kone_roccat_report roccat_report;
+
        roccat_report.event = kone_mouse_event_switch_profile;
        roccat_report.value = new_profile;
        roccat_report.key = 0;
@@ -163,6 +164,7 @@ static int kone_set_settings(struct usb_device *usb_dev,
                struct kone_settings const *settings)
 {
        int retval;
+
        retval = kone_send(usb_dev, kone_command_settings,
                        settings, sizeof(struct kone_settings));
        if (retval)
@@ -387,7 +389,7 @@ static struct bin_attribute bin_attr_profile##number = {    \
        .read = kone_sysfs_read_profilex,                       \
        .write = kone_sysfs_write_profilex,                     \
        .private = &profile_numbers[number-1],                  \
-};
+}
 PROFILE_ATTR(1);
 PROFILE_ATTR(2);
 PROFILE_ATTR(3);
@@ -456,6 +458,7 @@ static ssize_t kone_sysfs_show_tcu(struct device *dev,
 static int kone_tcu_command(struct usb_device *usb_dev, int number)
 {
        unsigned char value;
+
        value = number;
        return kone_send(usb_dev, kone_command_calibrate, &value, 1);
 }
@@ -697,10 +700,8 @@ static int kone_init_specials(struct hid_device *hdev)
                        == USB_INTERFACE_PROTOCOL_MOUSE) {
 
                kone = kzalloc(sizeof(*kone), GFP_KERNEL);
-               if (!kone) {
-                       hid_err(hdev, "can't alloc device descriptor\n");
+               if (!kone)
                        return -ENOMEM;
-               }
                hid_set_drvdata(hdev, kone);
 
                retval = kone_init_kone_device_struct(usb_dev, kone);
index 69cca14..5632c54 100644 (file)
@@ -7,7 +7,7 @@
  *  (This module is based on "hid-ortek".)
  *  Copyright (c) 2012 Andreas Hübner
  *
- *  R.A.T.7, M.M.O.7 (USB gaming mice):
+ *  R.A.T.7, R.A.T.9, M.M.O.7 (USB gaming mice):
  *  Fixes the mode button which cycles through three constantly pressed
  *  buttons. All three press events are mapped to one button and the
  *  missing release event is generated immediately.
@@ -179,6 +179,8 @@ static const struct hid_device_id saitek_devices[] = {
                .driver_data = SAITEK_FIX_PS1000 },
        { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7),
                .driver_data = SAITEK_RELEASE_MODE_RAT7 },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9),
+               .driver_data = SAITEK_RELEASE_MODE_RAT7 },
        { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7),
                .driver_data = SAITEK_RELEASE_MODE_MMO7 },
        { }
index bc4269e..31e9d25 100644 (file)
@@ -798,6 +798,12 @@ union sixaxis_output_report_01 {
        __u8 buf[36];
 };
 
+#define DS4_REPORT_0x02_SIZE 37
+#define DS4_REPORT_0x05_SIZE 32
+#define DS4_REPORT_0x11_SIZE 78
+#define DS4_REPORT_0x81_SIZE 7
+#define SIXAXIS_REPORT_0xF2_SIZE 18
+
 static spinlock_t sony_dev_list_lock;
 static LIST_HEAD(sony_device_list);
 static DEFINE_IDA(sony_device_id_allocator);
@@ -811,6 +817,7 @@ struct sony_sc {
        struct work_struct state_worker;
        struct power_supply battery;
        int device_id;
+       __u8 *output_report_dmabuf;
 
 #ifdef CONFIG_SONY_FF
        __u8 left;
@@ -1142,9 +1149,20 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
 
 static int sixaxis_set_operational_bt(struct hid_device *hdev)
 {
-       unsigned char buf[] = { 0xf4,  0x42, 0x03, 0x00, 0x00 };
-       return hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
+       static const __u8 report[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
+       __u8 *buf;
+       int ret;
+
+       buf = kmemdup(report, sizeof(report), GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(report),
                                  HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+
+       kfree(buf);
+
+       return ret;
 }
 
 /*
@@ -1153,10 +1171,19 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev)
  */
 static int dualshock4_set_operational_bt(struct hid_device *hdev)
 {
-       __u8 buf[37] = { 0 };
+       __u8 *buf;
+       int ret;
+
+       buf = kmalloc(DS4_REPORT_0x02_SIZE, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
 
-       return hid_hw_raw_request(hdev, 0x02, buf, sizeof(buf),
+       ret = hid_hw_raw_request(hdev, 0x02, buf, DS4_REPORT_0x02_SIZE,
                                HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+
+       kfree(buf);
+
+       return ret;
 }
 
 static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
@@ -1471,9 +1498,7 @@ error_leds:
 
 static void sixaxis_state_worker(struct work_struct *work)
 {
-       struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
-       int n;
-       union sixaxis_output_report_01 report = {
+       static const union sixaxis_output_report_01 default_report = {
                .buf = {
                        0x01,
                        0x00, 0xff, 0x00, 0xff, 0x00,
@@ -1485,20 +1510,27 @@ static void sixaxis_state_worker(struct work_struct *work)
                        0x00, 0x00, 0x00, 0x00, 0x00
                }
        };
+       struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
+       struct sixaxis_output_report *report =
+               (struct sixaxis_output_report *)sc->output_report_dmabuf;
+       int n;
+
+       /* Initialize the report with default values */
+       memcpy(report, &default_report, sizeof(struct sixaxis_output_report));
 
 #ifdef CONFIG_SONY_FF
-       report.data.rumble.right_motor_on = sc->right ? 1 : 0;
-       report.data.rumble.left_motor_force = sc->left;
+       report->rumble.right_motor_on = sc->right ? 1 : 0;
+       report->rumble.left_motor_force = sc->left;
 #endif
 
-       report.data.leds_bitmap |= sc->led_state[0] << 1;
-       report.data.leds_bitmap |= sc->led_state[1] << 2;
-       report.data.leds_bitmap |= sc->led_state[2] << 3;
-       report.data.leds_bitmap |= sc->led_state[3] << 4;
+       report->leds_bitmap |= sc->led_state[0] << 1;
+       report->leds_bitmap |= sc->led_state[1] << 2;
+       report->leds_bitmap |= sc->led_state[2] << 3;
+       report->leds_bitmap |= sc->led_state[3] << 4;
 
        /* Set flag for all leds off, required for 3rd party INTEC controller */
-       if ((report.data.leds_bitmap & 0x1E) == 0)
-               report.data.leds_bitmap |= 0x20;
+       if ((report->leds_bitmap & 0x1E) == 0)
+               report->leds_bitmap |= 0x20;
 
        /*
         * The LEDs in the report are indexed in reverse order to their
@@ -1511,28 +1543,30 @@ static void sixaxis_state_worker(struct work_struct *work)
         */
        for (n = 0; n < 4; n++) {
                if (sc->led_delay_on[n] || sc->led_delay_off[n]) {
-                       report.data.led[3 - n].duty_off = sc->led_delay_off[n];
-                       report.data.led[3 - n].duty_on = sc->led_delay_on[n];
+                       report->led[3 - n].duty_off = sc->led_delay_off[n];
+                       report->led[3 - n].duty_on = sc->led_delay_on[n];
                }
        }
 
-       hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf,
-                       sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
+       hid_hw_raw_request(sc->hdev, report->report_id, (__u8 *)report,
+                       sizeof(struct sixaxis_output_report),
+                       HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
 }
 
 static void dualshock4_state_worker(struct work_struct *work)
 {
        struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
        struct hid_device *hdev = sc->hdev;
+       __u8 *buf = sc->output_report_dmabuf;
        int offset;
 
-       __u8 buf[78] = { 0 };
-
        if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
+               memset(buf, 0, DS4_REPORT_0x05_SIZE);
                buf[0] = 0x05;
                buf[1] = 0xFF;
                offset = 4;
        } else {
+               memset(buf, 0, DS4_REPORT_0x11_SIZE);
                buf[0] = 0x11;
                buf[1] = 0xB0;
                buf[3] = 0x0F;
@@ -1560,12 +1594,33 @@ static void dualshock4_state_worker(struct work_struct *work)
        buf[offset++] = sc->led_delay_off[3];
 
        if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
-               hid_hw_output_report(hdev, buf, 32);
+               hid_hw_output_report(hdev, buf, DS4_REPORT_0x05_SIZE);
        else
-               hid_hw_raw_request(hdev, 0x11, buf, 78,
+               hid_hw_raw_request(hdev, 0x11, buf, DS4_REPORT_0x11_SIZE,
                                HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
 }
 
+static int sony_allocate_output_report(struct sony_sc *sc)
+{
+       if (sc->quirks & SIXAXIS_CONTROLLER)
+               sc->output_report_dmabuf =
+                       kmalloc(sizeof(union sixaxis_output_report_01),
+                               GFP_KERNEL);
+       else if (sc->quirks & DUALSHOCK4_CONTROLLER_BT)
+               sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x11_SIZE,
+                                               GFP_KERNEL);
+       else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
+               sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x05_SIZE,
+                                               GFP_KERNEL);
+       else
+               return 0;
+
+       if (!sc->output_report_dmabuf)
+               return -ENOMEM;
+
+       return 0;
+}
+
 #ifdef CONFIG_SONY_FF
 static int sony_play_effect(struct input_dev *dev, void *data,
                            struct ff_effect *effect)
@@ -1754,6 +1809,7 @@ static int sony_get_bt_devaddr(struct sony_sc *sc)
 
 static int sony_check_add(struct sony_sc *sc)
 {
+       __u8 *buf = NULL;
        int n, ret;
 
        if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
@@ -1769,36 +1825,44 @@ static int sony_check_add(struct sony_sc *sc)
                        return 0;
                }
        } else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
-               __u8 buf[7];
+               buf = kmalloc(DS4_REPORT_0x81_SIZE, GFP_KERNEL);
+               if (!buf)
+                       return -ENOMEM;
 
                /*
                 * The MAC address of a DS4 controller connected via USB can be
                 * retrieved with feature report 0x81. The address begins at
                 * offset 1.
                 */
-               ret = hid_hw_raw_request(sc->hdev, 0x81, buf, sizeof(buf),
-                               HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+               ret = hid_hw_raw_request(sc->hdev, 0x81, buf,
+                               DS4_REPORT_0x81_SIZE, HID_FEATURE_REPORT,
+                               HID_REQ_GET_REPORT);
 
-               if (ret != 7) {
+               if (ret != DS4_REPORT_0x81_SIZE) {
                        hid_err(sc->hdev, "failed to retrieve feature report 0x81 with the DualShock 4 MAC address\n");
-                       return ret < 0 ? ret : -EINVAL;
+                       ret = ret < 0 ? ret : -EINVAL;
+                       goto out_free;
                }
 
                memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
        } else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
-               __u8 buf[18];
+               buf = kmalloc(SIXAXIS_REPORT_0xF2_SIZE, GFP_KERNEL);
+               if (!buf)
+                       return -ENOMEM;
 
                /*
                 * The MAC address of a Sixaxis controller connected via USB can
                 * be retrieved with feature report 0xf2. The address begins at
                 * offset 4.
                 */
-               ret = hid_hw_raw_request(sc->hdev, 0xf2, buf, sizeof(buf),
-                               HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+               ret = hid_hw_raw_request(sc->hdev, 0xf2, buf,
+                               SIXAXIS_REPORT_0xF2_SIZE, HID_FEATURE_REPORT,
+                               HID_REQ_GET_REPORT);
 
-               if (ret != 18) {
+               if (ret != SIXAXIS_REPORT_0xF2_SIZE) {
                        hid_err(sc->hdev, "failed to retrieve feature report 0xf2 with the Sixaxis MAC address\n");
-                       return ret < 0 ? ret : -EINVAL;
+                       ret = ret < 0 ? ret : -EINVAL;
+                       goto out_free;
                }
 
                /*
@@ -1811,7 +1875,13 @@ static int sony_check_add(struct sony_sc *sc)
                return 0;
        }
 
-       return sony_check_add_dev_list(sc);
+       ret = sony_check_add_dev_list(sc);
+
+out_free:
+
+       kfree(buf);
+
+       return ret;
 }
 
 static int sony_set_device_id(struct sony_sc *sc)
@@ -1895,6 +1965,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                return ret;
        }
 
+       ret = sony_allocate_output_report(sc);
+       if (ret < 0) {
+               hid_err(hdev, "failed to allocate the output report buffer\n");
+               goto err_stop;
+       }
+
        ret = sony_set_device_id(sc);
        if (ret < 0) {
                hid_err(hdev, "failed to allocate the device id\n");
@@ -1984,6 +2060,7 @@ err_stop:
        if (sc->quirks & SONY_BATTERY_SUPPORT)
                sony_battery_remove(sc);
        sony_cancel_work_sync(sc);
+       kfree(sc->output_report_dmabuf);
        sony_remove_dev_list(sc);
        sony_release_device_id(sc);
        hid_hw_stop(hdev);
@@ -2004,6 +2081,8 @@ static void sony_remove(struct hid_device *hdev)
 
        sony_cancel_work_sync(sc);
 
+       kfree(sc->output_report_dmabuf);
+
        sony_remove_dev_list(sc);
 
        sony_release_device_id(sc);
@@ -2034,6 +2113,9 @@ static const struct hid_device_id sony_devices[] = {
        /* Logitech Harmony Adapter for PS3 */
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3),
                .driver_data = PS3REMOTE },
+       /* SMK-Link PS3 BD Remote Control */
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE),
+               .driver_data = PS3REMOTE },
        /* Sony Dualshock 4 controllers for PS4 */
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER),
                .driver_data = DUALSHOCK4_CONTROLLER_USB },
index c66e6ac..fbd0781 100644 (file)
@@ -370,7 +370,7 @@ static int i2c_hid_hwreset(struct i2c_client *client)
 static void i2c_hid_get_input(struct i2c_hid *ihid)
 {
        int ret, ret_size;
-       int size = le16_to_cpu(ihid->hdesc.wMaxInputLength);
+       int size = ihid->bufsize;
 
        ret = i2c_master_recv(ihid->client, ihid->inbuf, size);
        if (ret != size) {
index ca6849a..bfbe1be 100644 (file)
@@ -278,18 +278,20 @@ static void hid_irq_in(struct urb *urb)
                usbhid->retry_delay = 0;
                if ((hid->quirks & HID_QUIRK_ALWAYS_POLL) && !hid->open)
                        break;
-               hid_input_report(urb->context, HID_INPUT_REPORT,
-                                urb->transfer_buffer,
-                                urb->actual_length, 1);
-               /*
-                * autosuspend refused while keys are pressed
-                * because most keyboards don't wake up when
-                * a key is released
-                */
-               if (hid_check_keys_pressed(hid))
-                       set_bit(HID_KEYS_PRESSED, &usbhid->iofl);
-               else
-                       clear_bit(HID_KEYS_PRESSED, &usbhid->iofl);
+               if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
+                       hid_input_report(urb->context, HID_INPUT_REPORT,
+                                        urb->transfer_buffer,
+                                        urb->actual_length, 1);
+                       /*
+                        * autosuspend refused while keys are pressed
+                        * because most keyboards don't wake up when
+                        * a key is released
+                        */
+                       if (hid_check_keys_pressed(hid))
+                               set_bit(HID_KEYS_PRESSED, &usbhid->iofl);
+                       else
+                               clear_bit(HID_KEYS_PRESSED, &usbhid->iofl);
+               }
                break;
        case -EPIPE:            /* stall */
                usbhid_mark_busy(usbhid);
@@ -338,8 +340,7 @@ static int hid_submit_out(struct hid_device *hid)
        report = usbhid->out[usbhid->outtail].report;
        raw_report = usbhid->out[usbhid->outtail].raw_report;
 
-       usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) +
-                                                1 + (report->id > 0);
+       usbhid->urbout->transfer_buffer_length = hid_report_len(report);
        usbhid->urbout->dev = hid_to_usb_dev(hid);
        if (raw_report) {
                memcpy(usbhid->outbuf, raw_report,
@@ -688,6 +689,7 @@ int usbhid_open(struct hid_device *hid)
                        goto done;
                }
                usbhid->intf->needs_remote_wakeup = 1;
+               set_bit(HID_RESUME_RUNNING, &usbhid->iofl);
                res = hid_start_in(hid);
                if (res) {
                        if (res != -ENOSPC) {
@@ -701,6 +703,15 @@ int usbhid_open(struct hid_device *hid)
                        }
                }
                usb_autopm_put_interface(usbhid->intf);
+
+               /*
+                * In case events are generated while nobody was listening,
+                * some are released when the device is re-opened.
+                * Wait 50 msec for the queue to empty before allowing events
+                * to go through hid.
+                */
+               msleep(50);
+               clear_bit(HID_RESUME_RUNNING, &usbhid->iofl);
        }
 done:
        mutex_unlock(&hid_open_mut);
index 552671e..dc89be9 100644 (file)
@@ -73,11 +73,13 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL },
        { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B, HID_QUIRK_ALWAYS_POLL },
        { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_0103, HID_QUIRK_ALWAYS_POLL },
+       { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_010c, HID_QUIRK_ALWAYS_POLL },
        { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F, HID_QUIRK_ALWAYS_POLL },
        { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
index f633c24..807922b 100644 (file)
@@ -52,6 +52,7 @@ struct usb_interface *usbhid_find_interface(int minor);
 #define HID_STARTED            8
 #define HID_KEYS_PRESSED       10
 #define HID_NO_BANDWIDTH       11
+#define HID_RESUME_RUNNING     12
 
 /*
  * USB-specific HID struct, to be pointed to
index 0cc5344..7db4328 100644 (file)
@@ -140,7 +140,7 @@ extern const struct hid_device_id wacom_ids[];
 
 void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
 void wacom_setup_device_quirks(struct wacom_features *features);
-int wacom_setup_input_capabilities(struct input_dev *input_dev,
+int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
                                   struct wacom_wac *wacom_wac);
 int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
                                       struct wacom_wac *wacom_wac);
index 8593047..6542029 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "wacom_wac.h"
 #include "wacom.h"
+#include <linux/input/mt.h>
 
 #define WAC_MSG_RETRIES                5
 
@@ -70,22 +71,15 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
 static int wacom_open(struct input_dev *dev)
 {
        struct wacom *wacom = input_get_drvdata(dev);
-       int retval;
-
-       mutex_lock(&wacom->lock);
-       retval = hid_hw_open(wacom->hdev);
-       mutex_unlock(&wacom->lock);
 
-       return retval;
+       return hid_hw_open(wacom->hdev);
 }
 
 static void wacom_close(struct input_dev *dev)
 {
        struct wacom *wacom = input_get_drvdata(dev);
 
-       mutex_lock(&wacom->lock);
        hid_hw_close(wacom->hdev);
-       mutex_unlock(&wacom->lock);
 }
 
 /*
@@ -192,9 +186,15 @@ static void wacom_usage_mapping(struct hid_device *hdev,
        if (!pen && !finger)
                return;
 
-       if (finger && !features->touch_max)
-               /* touch device at least supports one touch point */
-               features->touch_max = 1;
+       /*
+        * Bamboo models do not support HID_DG_CONTACTMAX.
+        * And, Bamboo Pen only descriptor contains touch.
+        */
+       if (features->type != BAMBOO_PT) {
+               /* ISDv4 touch devices at least supports one touch point */
+               if (finger && !features->touch_max)
+                       features->touch_max = 1;
+       }
 
        switch (usage->hid) {
        case HID_GD_X:
@@ -230,6 +230,21 @@ static void wacom_usage_mapping(struct hid_device *hdev,
                wacom_wac_usage_mapping(hdev, field, usage);
 }
 
+static void wacom_post_parse_hid(struct hid_device *hdev,
+                                struct wacom_features *features)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+
+       if (features->type == HID_GENERIC) {
+               /* Any last-minute generic device setup */
+               if (features->touch_max > 1) {
+                       input_mt_init_slots(wacom_wac->input, wacom_wac->features.touch_max,
+                                   INPUT_MT_DIRECT);
+               }
+       }
+}
+
 static void wacom_parse_hid(struct hid_device *hdev,
                           struct wacom_features *features)
 {
@@ -264,6 +279,8 @@ static void wacom_parse_hid(struct hid_device *hdev,
                                wacom_usage_mapping(hdev, hreport->field[i],
                                                hreport->field[i]->usage + j);
        }
+
+       wacom_post_parse_hid(hdev, features);
 }
 
 static int wacom_hid_set_device_mode(struct hid_device *hdev)
@@ -1129,7 +1146,7 @@ static void wacom_clean_inputs(struct wacom *wacom)
                        input_free_device(wacom->wacom_wac.input);
        }
        if (wacom->wacom_wac.pad_input) {
-               if (wacom->wacom_wac.input_registered)
+               if (wacom->wacom_wac.pad_registered)
                        input_unregister_device(wacom->wacom_wac.pad_input);
                else
                        input_free_device(wacom->wacom_wac.pad_input);
@@ -1151,13 +1168,13 @@ static int wacom_register_inputs(struct wacom *wacom)
        if (!input_dev || !pad_input_dev)
                return -EINVAL;
 
-       error = wacom_setup_input_capabilities(input_dev, wacom_wac);
-       if (error)
-               return error;
-
-       error = input_register_device(input_dev);
-       if (error)
-               return error;
+       error = wacom_setup_pentouch_input_capabilities(input_dev, wacom_wac);
+       if (!error) {
+               error = input_register_device(input_dev);
+               if (error)
+                       return error;
+               wacom_wac->input_registered = true;
+       }
 
        error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac);
        if (error) {
@@ -1169,22 +1186,23 @@ static int wacom_register_inputs(struct wacom *wacom)
                error = input_register_device(pad_input_dev);
                if (error)
                        goto fail_register_pad_input;
+               wacom_wac->pad_registered = true;
 
                error = wacom_initialize_leds(wacom);
                if (error)
                        goto fail_leds;
        }
 
-       wacom_wac->input_registered = true;
-
        return 0;
 
 fail_leds:
        input_unregister_device(pad_input_dev);
        pad_input_dev = NULL;
+       wacom_wac->pad_registered = false;
 fail_register_pad_input:
        input_unregister_device(input_dev);
        wacom_wac->input = NULL;
+       wacom_wac->input_registered = false;
        return error;
 }
 
@@ -1321,12 +1339,6 @@ static void wacom_calculate_res(struct wacom_features *features)
                                                    features->unitExpo);
 }
 
-static int wacom_hid_report_len(struct hid_report *report)
-{
-       /* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */
-       return ((report->size - 1) >> 3) + 1 + (report->id > 0);
-}
-
 static size_t wacom_compute_pktlen(struct hid_device *hdev)
 {
        struct hid_report_enum *report_enum;
@@ -1336,7 +1348,7 @@ static size_t wacom_compute_pktlen(struct hid_device *hdev)
        report_enum = hdev->report_enum + HID_INPUT_REPORT;
 
        list_for_each_entry(report, &report_enum->report_list, list) {
-               size_t report_size = wacom_hid_report_len(report);
+               size_t report_size = hid_report_len(report);
                if (report_size > size)
                        size = report_size;
        }
index 586b240..ac7447c 100644 (file)
 #define WACOM_INTUOS_RES       100
 #define WACOM_INTUOS3_RES      200
 
+/* Newer Cintiq and DTU have an offset between tablet and screen areas */
+#define WACOM_DTU_OFFSET       200
+#define WACOM_CINTIQ_OFFSET    400
+
 /*
  * Scale factor relating reported contact size to logical contact area.
  * 2^14/pi is a good approximation on Intuos5 and 3rd-gen Bamboo
@@ -600,8 +604,8 @@ static void wacom_intuos_general(struct wacom_wac *wacom)
                }
                input_report_abs(input, ABS_PRESSURE, t);
                input_report_abs(input, ABS_TILT_X,
-                               ((data[7] << 1) & 0x7e) | (data[8] >> 7));
-               input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
+                                (((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64);
+               input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64);
                input_report_key(input, BTN_STYLUS, data[1] & 2);
                input_report_key(input, BTN_STYLUS2, data[1] & 4);
                input_report_key(input, BTN_TOUCH, t > 10);
@@ -612,8 +616,8 @@ static void wacom_intuos_general(struct wacom_wac *wacom)
                input_report_abs(input, ABS_WHEEL,
                                (data[6] << 2) | ((data[7] >> 6) & 3));
                input_report_abs(input, ABS_TILT_X,
-                               ((data[7] << 1) & 0x7e) | (data[8] >> 7));
-               input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
+                                (((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64);
+               input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64);
        }
 }
 
@@ -915,8 +919,8 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
                                input_report_key(input, BTN_EXTRA,  data[6] & 0x10);
 
                                input_report_abs(input, ABS_TILT_X,
-                                       ((data[7] << 1) & 0x7e) | (data[8] >> 7));
-                               input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
+                                       (((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64);
+                               input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64);
                        } else {
                                /* 2D mouse packet */
                                input_report_key(input, BTN_LEFT,   data[8] & 0x04);
@@ -1377,11 +1381,12 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-       struct input_dev *input = wacom_wac->input;
+       struct wacom_features *features = &wacom_wac->features;
        unsigned touch_max = wacom_wac->features.touch_max;
 
        switch (usage->hid) {
        case HID_GD_X:
+               features->last_slot_field = usage->hid;
                if (touch_max == 1)
                        wacom_map_usage(wacom, usage, field, EV_ABS, ABS_X, 4);
                else
@@ -1389,6 +1394,7 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
                                        ABS_MT_POSITION_X, 4);
                break;
        case HID_GD_Y:
+               features->last_slot_field = usage->hid;
                if (touch_max == 1)
                        wacom_map_usage(wacom, usage, field, EV_ABS, ABS_Y, 4);
                else
@@ -1396,19 +1402,48 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
                                        ABS_MT_POSITION_Y, 4);
                break;
        case HID_DG_CONTACTID:
-               input_mt_init_slots(input, wacom_wac->features.touch_max,
-                       INPUT_MT_DIRECT);
+               features->last_slot_field = usage->hid;
                break;
        case HID_DG_INRANGE:
+               features->last_slot_field = usage->hid;
                break;
        case HID_DG_INVERT:
+               features->last_slot_field = usage->hid;
                break;
        case HID_DG_TIPSWITCH:
+               features->last_slot_field = usage->hid;
                wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOUCH, 0);
                break;
        }
 }
 
+static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
+               struct input_dev *input)
+{
+       struct hid_data *hid_data = &wacom_wac->hid_data;
+       bool mt = wacom_wac->features.touch_max > 1;
+       bool prox = hid_data->tipswitch &&
+                   !wacom_wac->shared->stylus_in_proximity;
+
+       if (mt) {
+               int slot;
+
+               slot = input_mt_get_slot_by_key(input, hid_data->id);
+               input_mt_slot(input, slot);
+               input_mt_report_slot_state(input, MT_TOOL_FINGER, prox);
+       }
+       else {
+               input_report_key(input, BTN_TOUCH, prox);
+       }
+
+       if (prox) {
+               input_report_abs(input, mt ? ABS_MT_POSITION_X : ABS_X,
+                                hid_data->x);
+               input_report_abs(input, mt ? ABS_MT_POSITION_Y : ABS_Y,
+                                hid_data->y);
+       }
+}
+
 static int wacom_wac_finger_event(struct hid_device *hdev,
                struct hid_field *field, struct hid_usage *usage, __s32 value)
 {
@@ -1431,36 +1466,35 @@ static int wacom_wac_finger_event(struct hid_device *hdev,
        }
 
 
+       if (usage->usage_index + 1 == field->report_count) {
+               if (usage->hid == wacom_wac->features.last_slot_field)
+                       wacom_wac_finger_slot(wacom_wac, wacom_wac->input);
+       }
+
        return 0;
 }
 
-static void wacom_wac_finger_mt_report(struct wacom_wac *wacom_wac,
-               struct input_dev *input, bool touch)
+static int wacom_wac_finger_count_touches(struct hid_device *hdev)
 {
-       int slot;
-       struct hid_data *hid_data = &wacom_wac->hid_data;
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct input_dev *input = wacom_wac->input;
+       unsigned touch_max = wacom_wac->features.touch_max;
+       int count = 0;
+       int i;
 
-       slot = input_mt_get_slot_by_key(input, hid_data->id);
+       if (touch_max == 1)
+               return wacom_wac->hid_data.tipswitch &&
+                      !wacom_wac->shared->stylus_in_proximity;
 
-       input_mt_slot(input, slot);
-       input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
-       if (touch) {
-               input_report_abs(input, ABS_MT_POSITION_X, hid_data->x);
-               input_report_abs(input, ABS_MT_POSITION_Y, hid_data->y);
+       for (i = 0; i < input->mt->num_slots; i++) {
+               struct input_mt_slot *ps = &input->mt->slots[i];
+               int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
+               if (id >= 0)
+                       count++;
        }
-       input_mt_sync_frame(input);
-}
 
-static void wacom_wac_finger_single_touch_report(struct wacom_wac *wacom_wac,
-               struct input_dev *input, bool touch)
-{
-       struct hid_data *hid_data = &wacom_wac->hid_data;
-
-       if (touch) {
-               input_report_abs(input, ABS_X, hid_data->x);
-               input_report_abs(input, ABS_Y, hid_data->y);
-       }
-       input_report_key(input, BTN_TOUCH, touch);
+       return count;
 }
 
 static void wacom_wac_finger_report(struct hid_device *hdev,
@@ -1469,24 +1503,23 @@ static void wacom_wac_finger_report(struct hid_device *hdev,
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
        struct input_dev *input = wacom_wac->input;
-       bool touch = wacom_wac->hid_data.tipswitch &&
-                    !wacom_wac->shared->stylus_in_proximity;
        unsigned touch_max = wacom_wac->features.touch_max;
 
        if (touch_max > 1)
-               wacom_wac_finger_mt_report(wacom_wac, input, touch);
-       else
-               wacom_wac_finger_single_touch_report(wacom_wac, input, touch);
+               input_mt_sync_frame(input);
+
        input_sync(input);
 
        /* keep touch state for pen event */
-       wacom_wac->shared->touch_down = touch;
+       wacom_wac->shared->touch_down = wacom_wac_finger_count_touches(hdev);
 }
 
 #define WACOM_PEN_FIELD(f)     (((f)->logical == HID_DG_STYLUS) || \
-                                ((f)->physical == HID_DG_STYLUS))
+                                ((f)->physical == HID_DG_STYLUS) || \
+                                ((f)->application == HID_DG_PEN))
 #define WACOM_FINGER_FIELD(f)  (((f)->logical == HID_DG_FINGER) || \
-                                ((f)->physical == HID_DG_FINGER))
+                                ((f)->physical == HID_DG_FINGER) || \
+                                ((f)->application == HID_DG_TOUCHSCREEN))
 
 void wacom_wac_usage_mapping(struct hid_device *hdev,
                struct hid_field *field, struct hid_usage *usage)
@@ -1681,7 +1714,9 @@ static int wacom_bpt_pen(struct wacom_wac *wacom)
            return 0;
 
        if (data[0] == WACOM_REPORT_USB) {
-               if (features->type == INTUOSHT && features->touch_max) {
+               if (features->type == INTUOSHT &&
+                   wacom->shared->touch_input &&
+                   features->touch_max) {
                        input_report_switch(wacom->shared->touch_input,
                                            SW_MUTE_DEVICE, data[8] & 0x40);
                        input_sync(wacom->shared->touch_input);
@@ -1774,7 +1809,8 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
                int pid, battery, ps_connected;
 
                if ((wacom->shared->type == INTUOSHT) &&
-                               wacom->shared->touch_max) {
+                   wacom->shared->touch_input &&
+                   wacom->shared->touch_max) {
                        input_report_switch(wacom->shared->touch_input,
                                        SW_MUTE_DEVICE, data[5] & 0x40);
                        input_sync(wacom->shared->touch_input);
@@ -1838,6 +1874,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
                break;
 
        case DTUS:
+       case DTUSX:
                sync = wacom_dtus_irq(wacom_wac);
                break;
 
@@ -1926,8 +1963,10 @@ static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
        input_set_abs_params(input_dev, ABS_DISTANCE,
                             0, wacom_wac->features.distance_max, 0, 0);
        input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
-       input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
-       input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
+       input_set_abs_params(input_dev, ABS_TILT_X, -64, 63, 0, 0);
+       input_abs_set_res(input_dev, ABS_TILT_X, 57);
+       input_set_abs_params(input_dev, ABS_TILT_Y, -64, 63, 0, 0);
+       input_abs_set_res(input_dev, ABS_TILT_Y, 57);
 }
 
 static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
@@ -1947,6 +1986,7 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
        __set_bit(BTN_TOOL_LENS, input_dev->keybit);
 
        input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
+       input_abs_set_res(input_dev, ABS_RZ, 287);
        input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
 }
 
@@ -2029,7 +2069,7 @@ static void wacom_abs_set_axis(struct input_dev *input_dev,
        }
 }
 
-int wacom_setup_input_capabilities(struct input_dev *input_dev,
+int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
                                   struct wacom_wac *wacom_wac)
 {
        struct wacom_features *features = &wacom_wac->features;
@@ -2047,9 +2087,6 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
 
        switch (features->type) {
        case WACOM_MO:
-               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
-               /* fall through */
-
        case WACOM_G4:
                /* fall through */
 
@@ -2092,6 +2129,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
 
        case WACOM_24HD:
                input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               input_abs_set_res(input_dev, ABS_Z, 287);
                input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0);
                /* fall through */
 
@@ -2106,6 +2144,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
        case WACOM_BEE:
        case CINTIQ:
                input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               input_abs_set_res(input_dev, ABS_Z, 287);
 
                __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
 
@@ -2114,6 +2153,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
 
        case WACOM_13HD:
                input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               input_abs_set_res(input_dev, ABS_Z, 287);
                __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
                wacom_setup_cintiq(wacom_wac);
                break;
@@ -2122,6 +2162,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
        case INTUOS3L:
        case INTUOS3S:
                input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               input_abs_set_res(input_dev, ABS_Z, 287);
                /* fall through */
 
        case INTUOS:
@@ -2144,6 +2185,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
                                              0, 0);
 
                        input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+                       input_abs_set_res(input_dev, ABS_Z, 287);
 
                        wacom_setup_intuos(wacom_wac);
                } else if (features->device_type == BTN_TOOL_FINGER) {
@@ -2162,6 +2204,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
        case INTUOS4L:
        case INTUOS4S:
                input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               input_abs_set_res(input_dev, ABS_Z, 287);
                wacom_setup_intuos(wacom_wac);
 
                __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
@@ -2196,6 +2239,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
                /* fall through */
 
        case DTUS:
+       case DTUSX:
        case PL:
        case DTU:
                __set_bit(BTN_TOOL_PEN, input_dev->keybit);
@@ -2246,6 +2290,9 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
                                __clear_bit(ABS_X, input_dev->absbit);
                                __clear_bit(ABS_Y, input_dev->absbit);
                                __clear_bit(BTN_TOUCH, input_dev->keybit);
+
+                               /* PAD is setup by wacom_setup_pad_input_capabilities later */
+                               return 1;
                        }
                } else if (features->device_type == BTN_TOOL_PEN) {
                        __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
@@ -2261,6 +2308,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
 
        case CINTIQ_HYBRID:
                input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               input_abs_set_res(input_dev, ABS_Z, 287);
                __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
 
                wacom_setup_cintiq(wacom_wac);
@@ -2303,9 +2351,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
 
        case WACOM_G4:
                __set_bit(BTN_BACK, input_dev->keybit);
-               __set_bit(BTN_LEFT, input_dev->keybit);
                __set_bit(BTN_FORWARD, input_dev->keybit);
-               __set_bit(BTN_RIGHT, input_dev->keybit);
                input_set_capability(input_dev, EV_REL, REL_WHEEL);
                break;
 
@@ -2402,7 +2448,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
        case INTUOSPS:
                /* touch interface does not have the pad device */
                if (features->device_type != BTN_TOOL_PEN)
-                       return 1;
+                       return -ENODEV;
 
                for (i = 0; i < 7; i++)
                        __set_bit(BTN_0 + i, input_dev->keybit);
@@ -2446,8 +2492,10 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
        case INTUOSHT:
        case BAMBOO_PT:
                /* pad device is on the touch interface */
-               if (features->device_type != BTN_TOOL_FINGER)
-                       return 1;
+               if ((features->device_type != BTN_TOOL_FINGER) ||
+                   /* Bamboo Pen only tablet does not have pad */
+                   ((features->type == BAMBOO_PT) && !features->touch_max))
+                       return -ENODEV;
 
                __clear_bit(ABS_MISC, input_dev->absbit);
 
@@ -2460,7 +2508,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
 
        default:
                /* no pad supported */
-               return 1;
+               return -ENODEV;
        }
        return 0;
 }
@@ -2664,11 +2712,13 @@ static const struct wacom_features wacom_features_0x317 =
          INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0xF4 =
-       { "Wacom Cintiq 24HD", 104280, 65400, 2047, 63,
-         WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+       { "Wacom Cintiq 24HD", 104080, 65200, 2047, 63,
+         WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0xF8 =
-       { "Wacom Cintiq 24HD touch", 104280, 65400, 2047, 63, /* Pen */
-         WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
+       { "Wacom Cintiq 24HD touch", 104080, 65200, 2047, 63, /* Pen */
+         WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf6 };
 static const struct wacom_features wacom_features_0xF6 =
        { "Wacom Cintiq 24HD touch", .type = WACOM_24HDT, /* Touch */
@@ -2684,8 +2734,9 @@ static const struct wacom_features wacom_features_0xC6 =
        { "Wacom Cintiq 12WX", 53020, 33440, 1023, 63,
          WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
 static const struct wacom_features wacom_features_0x304 =
-       { "Wacom Cintiq 13HD", 59352, 33648, 1023, 63,
-         WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+       { "Wacom Cintiq 13HD", 59152, 33448, 1023, 63,
+         WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0xC7 =
        { "Wacom DTU1931", 37832, 30305, 511, 0,
          PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -2697,28 +2748,38 @@ static const struct wacom_features wacom_features_0xF0 =
        { "Wacom DTU1631", 34623, 19553, 511, 0,
          DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0xFB =
-       { "Wacom DTU1031", 22096, 13960, 511, 0,
-         DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+       { "Wacom DTU1031", 21896, 13760, 511, 0,
+         DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
+         WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
+static const struct wacom_features wacom_features_0x32F =
+       { "Wacom DTU1031X", 22472, 12728, 511, 0,
+         DTUSX, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
+         WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
 static const struct wacom_features wacom_features_0x57 =
        { "Wacom DTK2241", 95640, 54060, 2047, 63,
-         DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+         DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0x59 = /* Pen */
        { "Wacom DTH2242", 95640, 54060, 2047, 63,
-         DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
+         DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D };
 static const struct wacom_features wacom_features_0x5D = /* Touch */
        { "Wacom DTH2242",       .type = WACOM_24HDT,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10,
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0xCC =
-       { "Wacom Cintiq 21UX2", 87000, 65400, 2047, 63,
-         WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+       { "Wacom Cintiq 21UX2", 86800, 65200, 2047, 63,
+         WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0xFA =
-       { "Wacom Cintiq 22HD", 95640, 54060, 2047, 63,
-         WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+       { "Wacom Cintiq 22HD", 95440, 53860, 2047, 63,
+         WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0x5B =
-       { "Wacom Cintiq 22HDT", 95640, 54060, 2047, 63,
-         WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
+       { "Wacom Cintiq 22HDT", 95440, 53860, 2047, 63,
+         WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e };
 static const struct wacom_features wacom_features_0x5E =
        { "Wacom Cintiq 22HDT", .type = WACOM_24HDT,
@@ -2863,21 +2924,27 @@ static const struct wacom_features wacom_features_0x6004 =
        { "ISD-V4", 12800, 8000, 255, 0,
          TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x307 =
-       { "Wacom ISDv5 307", 59352, 33648, 2047, 63,
-         CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
+       { "Wacom ISDv5 307", 59152, 33448, 2047, 63,
+         CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 };
 static const struct wacom_features wacom_features_0x309 =
        { "Wacom ISDv5 309", .type = WACOM_24HDT, /* Touch */
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10,
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0x30A =
-       { "Wacom ISDv5 30A", 59352, 33648, 2047, 63,
-         CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
+       { "Wacom ISDv5 30A", 59152, 33448, 2047, 63,
+         CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30C };
 static const struct wacom_features wacom_features_0x30C =
        { "Wacom ISDv5 30C", .type = WACOM_24HDT, /* Touch */
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30A, .touch_max = 10,
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
+static const struct wacom_features wacom_features_0x323 =
+       { "Wacom Intuos P M", 21600, 13500, 1023, 31,
+         INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
+         .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 
 static const struct wacom_features wacom_features_HID_ANY_ID =
        { "Wacom HID", .type = HID_GENERIC };
@@ -3022,10 +3089,13 @@ const struct hid_device_id wacom_ids[] = {
        { USB_DEVICE_WACOM(0x314) },
        { USB_DEVICE_WACOM(0x315) },
        { USB_DEVICE_WACOM(0x317) },
+       { USB_DEVICE_WACOM(0x323) },
+       { USB_DEVICE_WACOM(0x32F) },
        { USB_DEVICE_WACOM(0x4001) },
        { USB_DEVICE_WACOM(0x4004) },
        { USB_DEVICE_WACOM(0x5000) },
        { USB_DEVICE_WACOM(0x5002) },
+       { USB_DEVICE_LENOVO(0x6004) },
 
        { USB_DEVICE_WACOM(HID_ANY_ID) },
        { }
index 0f0b85e..bfad815 100644 (file)
@@ -80,6 +80,7 @@ enum {
        PL,
        DTU,
        DTUS,
+       DTUSX,
        INTUOS,
        INTUOS3S,
        INTUOS3,
@@ -144,6 +145,7 @@ struct wacom_features {
        int pktlen;
        bool check_for_hid_type;
        int hid_type;
+       int last_slot_field;
 };
 
 struct wacom_shared {
@@ -183,6 +185,7 @@ struct wacom_wac {
        struct input_dev *input;
        struct input_dev *pad_input;
        bool input_registered;
+       bool pad_registered;
        int pid;
        int battery_capacity;
        int num_contacts_left;
index 39b4081..8e8bc4f 100644 (file)
@@ -100,6 +100,9 @@ static const struct usb_device_id usb_quirk_list[] = {
        { USB_DEVICE(0x04f3, 0x009b), .driver_info =
                        USB_QUIRK_DEVICE_QUALIFIER },
 
+       { USB_DEVICE(0x04f3, 0x010c), .driver_info =
+                       USB_QUIRK_DEVICE_QUALIFIER },
+
        { USB_DEVICE(0x04f3, 0x016f), .driver_info =
                        USB_QUIRK_DEVICE_QUALIFIER },
 
index 78ea9bf..06c4607 100644 (file)
@@ -234,6 +234,33 @@ struct hid_item {
 #define HID_DG_BARRELSWITCH    0x000d0044
 #define HID_DG_ERASER          0x000d0045
 #define HID_DG_TABLETPICK      0x000d0046
+
+#define HID_CP_CONSUMERCONTROL 0x000c0001
+#define HID_CP_NUMERICKEYPAD   0x000c0002
+#define HID_CP_PROGRAMMABLEBUTTONS     0x000c0003
+#define HID_CP_MICROPHONE      0x000c0004
+#define HID_CP_HEADPHONE       0x000c0005
+#define HID_CP_GRAPHICEQUALIZER        0x000c0006
+#define HID_CP_FUNCTIONBUTTONS 0x000c0036
+#define HID_CP_SELECTION       0x000c0080
+#define HID_CP_MEDIASELECTION  0x000c0087
+#define HID_CP_SELECTDISC      0x000c00ba
+#define HID_CP_PLAYBACKSPEED   0x000c00f1
+#define HID_CP_PROXIMITY       0x000c0109
+#define HID_CP_SPEAKERSYSTEM   0x000c0160
+#define HID_CP_CHANNELLEFT     0x000c0161
+#define HID_CP_CHANNELRIGHT    0x000c0162
+#define HID_CP_CHANNELCENTER   0x000c0163
+#define HID_CP_CHANNELFRONT    0x000c0164
+#define HID_CP_CHANNELCENTERFRONT      0x000c0165
+#define HID_CP_CHANNELSIDE     0x000c0166
+#define HID_CP_CHANNELSURROUND 0x000c0167
+#define HID_CP_CHANNELLOWFREQUENCYENHANCEMENT  0x000c0168
+#define HID_CP_CHANNELTOP      0x000c0169
+#define HID_CP_CHANNELUNKNOWN  0x000c016a
+#define HID_CP_APPLICATIONLAUNCHBUTTONS        0x000c0180
+#define HID_CP_GENERICGUIAPPLICATIONCONTROLS   0x000c0200
+
 #define HID_DG_CONFIDENCE      0x000d0047
 #define HID_DG_WIDTH           0x000d0048
 #define HID_DG_HEIGHT          0x000d0049
@@ -312,11 +339,8 @@ struct hid_item {
  * Vendor specific HID device groups
  */
 #define HID_GROUP_RMI                          0x0100
-
-/*
- * Vendor specific HID device groups
- */
 #define HID_GROUP_WACOM                                0x0101
+#define HID_GROUP_LOGITECH_DJ_DEVICE           0x0102
 
 /*
  * This is the global environment of the parser. This information is
@@ -1063,6 +1087,17 @@ static inline void hid_hw_wait(struct hid_device *hdev)
                hdev->ll_driver->wait(hdev);
 }
 
+/**
+ * hid_report_len - calculate the report length
+ *
+ * @report: the report we want to know the length
+ */
+static inline int hid_report_len(struct hid_report *report)
+{
+       /* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */
+       return ((report->size - 1) >> 3) + 1 + (report->id > 0);
+}
+
 int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
                int interrupt);