Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 4 Jun 2014 15:52:36 +0000 (08:52 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 4 Jun 2014 15:52:36 +0000 (08:52 -0700)
Pull HID patches from Jiri Kosina:
 - RMI driver for Synaptics touchpads, by Benjamin Tissoires, Andrew
   Duggan and Jiri Kosina
 - cleanup of hid-sony driver and improved support for Sixaxis and
   Dualshock 4, by Frank Praznik
 - other usual small fixes and support for new device IDs

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (29 commits)
  HID: thingm: thingm_fwinfo[] doesn't need to be global
  HID: core: add two new usages for digitizer
  HID: hid-sensor-hub: new device id and quirk for STM Sensor hub
  HID: usbhid: enable NO_INIT_REPORTS quirk for Semico USB Keykoard
  HID: hid-sensor-hub: Set report quirk for Microsoft Surface
  HID: debug: add labels for HID Sensor Usages
  HID: uhid: Use kmemdup instead of kmalloc + memcpy
  HID: rmi: do not handle touchscreens through hid-rmi
  HID: quirk for Saitek RAT7 and MMO7 mices' mode button
  HID: core: fix validation of report id 0
  HID: rmi: fix masks for x and w_x data
  HID: rmi: fix wrong struct field name
  HID: rmi: do not fetch more than 16 bytes in a query
  HID: rmi: check for the existence of some optional queries before reading query 12
  HID: i2c-hid: hid report descriptor retrieval changes
  HID: add missing hid usages
  HID: hid-sony - allow 3rd party INTEC controller to turn off all leds
  HID: sony: Add blink support to the Sixaxis and DualShock 4 LEDs
  HID: sony: Initialize the controller LEDs with a device ID value
  HID: sony: Use the controller Bluetooth MAC address as the unique value in the battery name string
  ...

18 files changed:
Documentation/ABI/testing/sysfs-driver-hid-thingm [deleted file]
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-core.c
drivers/hid/hid-debug.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-rmi.c [new file with mode: 0644]
drivers/hid/hid-saitek.c
drivers/hid/hid-sensor-hub.c
drivers/hid/hid-sony.c
drivers/hid/hid-thingm.c
drivers/hid/i2c-hid/i2c-hid.c
drivers/hid/uhid.c
drivers/hid/usbhid/hid-quirks.c
drivers/leds/Kconfig
include/linux/hid.h
include/uapi/linux/input.h

diff --git a/Documentation/ABI/testing/sysfs-driver-hid-thingm b/Documentation/ABI/testing/sysfs-driver-hid-thingm
deleted file mode 100644 (file)
index abcffee..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-What:          /sys/class/leds/blink1::<serial>/rgb
-Date:          January 2013
-Contact:       Vivien Didelot <vivien.didelot@savoirfairelinux.com>
-Description:   The ThingM blink1 is an USB RGB LED. The color notation is
-               3-byte hexadecimal. Read this attribute to get the last set
-               color. Write the 24-bit hexadecimal color to change the current
-               LED color. The default color is full white (0xFFFFFF).
-               For instance, set the color to green with: echo 00FF00 > rgb
-
-What:          /sys/class/leds/blink1::<serial>/fade
-Date:          January 2013
-Contact:       Vivien Didelot <vivien.didelot@savoirfairelinux.com>
-Description:   This attribute allows to set a fade time in milliseconds for
-               the next color change. Read the attribute to know the current
-               fade time. The default value is set to 0 (no fade time). For
-               instance, set a fade time of 2 seconds with: echo 2000 > fade
-
-What:          /sys/class/leds/blink1::<serial>/play
-Date:          January 2013
-Contact:       Vivien Didelot <vivien.didelot@savoirfairelinux.com>
-Description:   This attribute is used to play/pause the light patterns. Write 1
-               to start playing, 0 to stop. Reading this attribute returns the
-               current playing status.
index 7af9d0b..800c8b6 100644 (file)
@@ -608,7 +608,10 @@ config HID_SAITEK
        Support for Saitek devices that are not fully compliant with the
        HID standard.
 
-       Currently only supports the PS1000 controller.
+       Supported devices:
+       - PS1000 Dual Analog Pad
+       - R.A.T.7 Gaming Mouse
+       - M.M.O.7 Gaming Mouse
 
 config HID_SAMSUNG
        tristate "Samsung InfraRed remote control or keyboards"
@@ -657,6 +660,14 @@ config HID_SUNPLUS
        ---help---
        Support for Sunplus wireless desktop.
 
+config HID_RMI
+       tristate "Synaptics RMI4 device support"
+       depends on HID
+       ---help---
+       Support for Synaptics RMI4 touchpads.
+       Say Y here if you have a Synaptics RMI4 touchpads over i2c-hid or usbhid
+       and want support for its special functionalities.
+
 config HID_GREENASIA
        tristate "GreenAsia (Product ID 0x12) game controller support"
        depends on HID
index fc712dd..a6fa6ba 100644 (file)
@@ -97,6 +97,7 @@ obj-$(CONFIG_HID_ROCCAT)      += hid-roccat.o hid-roccat-common.o \
        hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
        hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \
        hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-ryos.o hid-roccat-savu.o
+obj-$(CONFIG_HID_RMI)          += hid-rmi.o
 obj-$(CONFIG_HID_SAITEK)       += hid-saitek.o
 obj-$(CONFIG_HID_SAMSUNG)      += hid-samsung.o
 obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
index da52279..8ed66fd 100644 (file)
@@ -779,6 +779,14 @@ 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))
+               /* hid-rmi should take care of them, not hid-generic */
+               hid->group = HID_GROUP_RMI;
+
        vfree(parser);
        return 0;
 }
@@ -842,7 +850,17 @@ struct hid_report *hid_validate_values(struct hid_device *hid,
         * ->numbered being checked, which may not always be the case when
         * drivers go to access report values.
         */
-       report = hid->report_enum[type].report_id_hash[id];
+       if (id == 0) {
+               /*
+                * Validating on id 0 means we should examine the first
+                * report in the list.
+                */
+               report = list_entry(
+                               hid->report_enum[type].report_list.next,
+                               struct hid_report, list);
+       } else {
+               report = hid->report_enum[type].report_id_hash[id];
+       }
        if (!report) {
                hid_err(hid, "missing %s %u\n", hid_report_names[type], id);
                return NULL;
@@ -1868,7 +1886,11 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
 #endif
+#if IS_ENABLED(CONFIG_HID_SAITEK)
        { 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) },
+#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) },
index 53b771d..84c3cb1 100644 (file)
@@ -165,6 +165,8 @@ static const struct hid_usage_entry hid_usage_table[] = {
     {0, 0x53, "DeviceIndex"},
     {0, 0x54, "ContactCount"},
     {0, 0x55, "ContactMaximumNumber"},
+    {0, 0x5A, "SecondaryBarrelSwitch"},
+    {0, 0x5B, "TransducerSerialNumber"},
   { 15, 0, "PhysicalInterfaceDevice" },
     {0, 0x00, "Undefined"},
     {0, 0x01, "Physical_Interface_Device"},
@@ -272,6 +274,85 @@ static const struct hid_usage_entry hid_usage_table[] = {
     {0, 0xAA, "Shared_Parameter_Blocks"},
     {0, 0xAB, "Create_New_Effect_Report"},
     {0, 0xAC, "RAM_Pool_Available"},
+  {  0x20, 0, "Sensor" },
+    { 0x20, 0x01, "Sensor" },
+    { 0x20, 0x10, "Biometric" },
+      { 0x20, 0x11, "BiometricHumanPresence" },
+      { 0x20, 0x12, "BiometricHumanProximity" },
+      { 0x20, 0x13, "BiometricHumanTouch" },
+    { 0x20, 0x20, "Electrical" },
+      { 0x20, 0x21, "ElectricalCapacitance" },
+      { 0x20, 0x22, "ElectricalCurrent" },
+      { 0x20, 0x23, "ElectricalPower" },
+      { 0x20, 0x24, "ElectricalInductance" },
+      { 0x20, 0x25, "ElectricalResistance" },
+      { 0x20, 0x26, "ElectricalVoltage" },
+      { 0x20, 0x27, "ElectricalPoteniometer" },
+      { 0x20, 0x28, "ElectricalFrequency" },
+      { 0x20, 0x29, "ElectricalPeriod" },
+    { 0x20, 0x30, "Environmental" },
+      { 0x20, 0x31, "EnvironmentalAtmosphericPressure" },
+      { 0x20, 0x32, "EnvironmentalHumidity" },
+      { 0x20, 0x33, "EnvironmentalTemperature" },
+      { 0x20, 0x34, "EnvironmentalWindDirection" },
+      { 0x20, 0x35, "EnvironmentalWindSpeed" },
+    { 0x20, 0x40, "Light" },
+      { 0x20, 0x41, "LightAmbientLight" },
+      { 0x20, 0x42, "LightConsumerInfrared" },
+    { 0x20, 0x50, "Location" },
+      { 0x20, 0x51, "LocationBroadcast" },
+      { 0x20, 0x52, "LocationDeadReckoning" },
+      { 0x20, 0x53, "LocationGPS" },
+      { 0x20, 0x54, "LocationLookup" },
+      { 0x20, 0x55, "LocationOther" },
+      { 0x20, 0x56, "LocationStatic" },
+      { 0x20, 0x57, "LocationTriangulation" },
+    { 0x20, 0x60, "Mechanical" },
+      { 0x20, 0x61, "MechanicalBooleanSwitch" },
+      { 0x20, 0x62, "MechanicalBooleanSwitchArray" },
+      { 0x20, 0x63, "MechanicalMultivalueSwitch" },
+      { 0x20, 0x64, "MechanicalForce" },
+      { 0x20, 0x65, "MechanicalPressure" },
+      { 0x20, 0x66, "MechanicalStrain" },
+      { 0x20, 0x67, "MechanicalWeight" },
+      { 0x20, 0x68, "MechanicalHapticVibrator" },
+      { 0x20, 0x69, "MechanicalHallEffectSwitch" },
+    { 0x20, 0x70, "Motion" },
+      { 0x20, 0x71, "MotionAccelerometer1D" },
+      { 0x20, 0x72, "MotionAccelerometer2D" },
+      { 0x20, 0x73, "MotionAccelerometer3D" },
+      { 0x20, 0x74, "MotionGyrometer1D" },
+      { 0x20, 0x75, "MotionGyrometer2D" },
+      { 0x20, 0x76, "MotionGyrometer3D" },
+      { 0x20, 0x77, "MotionMotionDetector" },
+      { 0x20, 0x78, "MotionSpeedometer" },
+      { 0x20, 0x79, "MotionAccelerometer" },
+      { 0x20, 0x7A, "MotionGyrometer" },
+    { 0x20, 0x80, "Orientation" },
+      { 0x20, 0x81, "OrientationCompass1D" },
+      { 0x20, 0x82, "OrientationCompass2D" },
+      { 0x20, 0x83, "OrientationCompass3D" },
+      { 0x20, 0x84, "OrientationInclinometer1D" },
+      { 0x20, 0x85, "OrientationInclinometer2D" },
+      { 0x20, 0x86, "OrientationInclinometer3D" },
+      { 0x20, 0x87, "OrientationDistance1D" },
+      { 0x20, 0x88, "OrientationDistance2D" },
+      { 0x20, 0x89, "OrientationDistance3D" },
+      { 0x20, 0x8A, "OrientationDeviceOrientation" },
+      { 0x20, 0x8B, "OrientationCompass" },
+      { 0x20, 0x8C, "OrientationInclinometer" },
+      { 0x20, 0x8D, "OrientationDistance" },
+    { 0x20, 0x90, "Scanner" },
+      { 0x20, 0x91, "ScannerBarcode" },
+      { 0x20, 0x91, "ScannerRFID" },
+      { 0x20, 0x91, "ScannerNFC" },
+    { 0x20, 0xA0, "Time" },
+      { 0x20, 0xA1, "TimeAlarmTimer" },
+      { 0x20, 0xA2, "TimeRealTimeClock" },
+    { 0x20, 0xE0, "Other" },
+      { 0x20, 0xE1, "OtherCustom" },
+      { 0x20, 0xE2, "OtherGeneric" },
+      { 0x20, 0xE3, "OtherGenericEnumerator" },
   { 0x84, 0, "Power Device" },
     { 0x84, 0x02, "PresentStatus" },
     { 0x84, 0x03, "ChangeStatus" },
@@ -855,6 +936,16 @@ static const char *keys[KEY_MAX + 1] = {
        [KEY_KBDILLUMDOWN] = "KbdIlluminationDown",
        [KEY_KBDILLUMUP] = "KbdIlluminationUp",
        [KEY_SWITCHVIDEOMODE] = "SwitchVideoMode",
+       [KEY_BUTTONCONFIG] = "ButtonConfig",
+       [KEY_TASKMANAGER] = "TaskManager",
+       [KEY_JOURNAL] = "Journal",
+       [KEY_CONTROLPANEL] = "ControlPanel",
+       [KEY_APPSELECT] = "AppSelect",
+       [KEY_SCREENSAVER] = "ScreenSaver",
+       [KEY_VOICECOMMAND] = "VoiceCommand",
+       [KEY_BRIGHTNESS_MIN] = "BrightnessMin",
+       [KEY_BRIGHTNESS_MAX] = "BrightnessMax",
+       [KEY_BRIGHTNESS_AUTO] = "BrightnessAuto",
 };
 
 static const char *relatives[REL_MAX + 1] = {
index 34bb220..6d00bb9 100644 (file)
 
 #define USB_VENDOR_ID_STM_0             0x0483
 #define USB_DEVICE_ID_STM_HID_SENSOR    0x91d1
+#define USB_DEVICE_ID_STM_HID_SENSOR_1  0x9100
 
 #define USB_VENDOR_ID_ION              0x15e4
 #define USB_DEVICE_ID_ICADE            0x0132
 #define USB_DEVICE_ID_MS_PRESENTER_8K_USB      0x0713
 #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K      0x0730
 #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500    0x076c
+#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_VENDOR_ID_MOJO             0x8282
 #define USB_DEVICE_ID_RETRO_ADAPTER    0x3201
 #define USB_VENDOR_ID_SAITEK           0x06a3
 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
 #define USB_DEVICE_ID_SAITEK_PS1000    0x0621
+#define USB_DEVICE_ID_SAITEK_RAT7      0x0cd7
+#define USB_DEVICE_ID_SAITEK_MMO7      0x0cd0
 
 #define USB_VENDOR_ID_SAMSUNG          0x0419
 #define USB_DEVICE_ID_SAMSUNG_IR_REMOTE        0x0001
 #define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE       0x0600
 
+#define USB_VENDOR_ID_SEMICO                   0x1a2c
+#define USB_DEVICE_ID_SEMICO_USB_KEYKOARD      0x0023
+
 #define USB_VENDOR_ID_SENNHEISER       0x1395
 #define USB_DEVICE_ID_SENNHEISER_BTD500USB     0x002c
 
index e7e8b19..2619f7f 100644 (file)
@@ -684,9 +684,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                        break;
 
                case 0x46: /* TabletPick */
+               case 0x5a: /* SecondaryBarrelSwitch */
                        map_key_clear(BTN_STYLUS2);
                        break;
 
+               case 0x5b: /* TransducerSerialNumber */
+                       set_bit(MSC_SERIAL, input->mscbit);
+                       break;
+
                default:  goto unknown;
                }
                break;
@@ -721,6 +726,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x06c: map_key_clear(KEY_YELLOW);          break;
                case 0x06d: map_key_clear(KEY_ZOOM);            break;
 
+               case 0x06f: map_key_clear(KEY_BRIGHTNESSUP);            break;
+               case 0x070: map_key_clear(KEY_BRIGHTNESSDOWN);          break;
+               case 0x072: map_key_clear(KEY_BRIGHTNESS_TOGGLE);       break;
+               case 0x073: map_key_clear(KEY_BRIGHTNESS_MIN);          break;
+               case 0x074: map_key_clear(KEY_BRIGHTNESS_MAX);          break;
+               case 0x075: map_key_clear(KEY_BRIGHTNESS_AUTO);         break;
+
                case 0x082: map_key_clear(KEY_VIDEO_NEXT);      break;
                case 0x083: map_key_clear(KEY_LAST);            break;
                case 0x084: map_key_clear(KEY_ENTER);           break;
@@ -761,6 +773,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x0bf: map_key_clear(KEY_SLOW);            break;
 
                case 0x0cd: map_key_clear(KEY_PLAYPAUSE);       break;
+               case 0x0cf: map_key_clear(KEY_VOICECOMMAND);    break;
                case 0x0e0: map_abs_clear(ABS_VOLUME);          break;
                case 0x0e2: map_key_clear(KEY_MUTE);            break;
                case 0x0e5: map_key_clear(KEY_BASSBOOST);       break;
@@ -768,6 +781,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x0ea: map_key_clear(KEY_VOLUMEDOWN);      break;
                case 0x0f5: map_key_clear(KEY_SLOW);            break;
 
+               case 0x181: map_key_clear(KEY_BUTTONCONFIG);    break;
                case 0x182: map_key_clear(KEY_BOOKMARKS);       break;
                case 0x183: map_key_clear(KEY_CONFIG);          break;
                case 0x184: map_key_clear(KEY_WORDPROCESSOR);   break;
@@ -781,6 +795,8 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x18c: map_key_clear(KEY_VOICEMAIL);       break;
                case 0x18d: map_key_clear(KEY_ADDRESSBOOK);     break;
                case 0x18e: map_key_clear(KEY_CALENDAR);        break;
+               case 0x18f: map_key_clear(KEY_TASKMANAGER);     break;
+               case 0x190: map_key_clear(KEY_JOURNAL);         break;
                case 0x191: map_key_clear(KEY_FINANCE);         break;
                case 0x192: map_key_clear(KEY_CALC);            break;
                case 0x193: map_key_clear(KEY_PLAYER);          break;
@@ -789,12 +805,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x199: map_key_clear(KEY_CHAT);            break;
                case 0x19c: map_key_clear(KEY_LOGOFF);          break;
                case 0x19e: map_key_clear(KEY_COFFEE);          break;
+               case 0x19f: map_key_clear(KEY_CONTROLPANEL);            break;
+               case 0x1a2: map_key_clear(KEY_APPSELECT);               break;
                case 0x1a3: map_key_clear(KEY_NEXT);            break;
                case 0x1a4: map_key_clear(KEY_PREVIOUS);        break;
                case 0x1a6: map_key_clear(KEY_HELP);            break;
                case 0x1a7: map_key_clear(KEY_DOCUMENTS);       break;
                case 0x1ab: map_key_clear(KEY_SPELLCHECK);      break;
                case 0x1ae: map_key_clear(KEY_KEYBOARD);        break;
+               case 0x1b1: map_key_clear(KEY_SCREENSAVER);             break;
                case 0x1b4: map_key_clear(KEY_FILE);            break;
                case 0x1b6: map_key_clear(KEY_IMAGES);          break;
                case 0x1b7: map_key_clear(KEY_AUDIO);           break;
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
new file mode 100644 (file)
index 0000000..2451c7e
--- /dev/null
@@ -0,0 +1,920 @@
+/*
+ *  Copyright (c) 2013 Andrew Duggan <aduggan@synaptics.com>
+ *  Copyright (c) 2013 Synaptics Incorporated
+ *  Copyright (c) 2014 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ *  Copyright (c) 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; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include "hid-ids.h"
+
+#define RMI_MOUSE_REPORT_ID            0x01 /* Mouse emulation Report */
+#define RMI_WRITE_REPORT_ID            0x09 /* Output Report */
+#define RMI_READ_ADDR_REPORT_ID                0x0a /* Output Report */
+#define RMI_READ_DATA_REPORT_ID                0x0b /* Input Report */
+#define RMI_ATTN_REPORT_ID             0x0c /* Input Report */
+#define RMI_SET_RMI_MODE_REPORT_ID     0x0f /* Feature Report */
+
+/* flags */
+#define RMI_READ_REQUEST_PENDING       BIT(0)
+#define RMI_READ_DATA_PENDING          BIT(1)
+#define RMI_STARTED                    BIT(2)
+
+enum rmi_mode_type {
+       RMI_MODE_OFF                    = 0,
+       RMI_MODE_ATTN_REPORTS           = 1,
+       RMI_MODE_NO_PACKED_ATTN_REPORTS = 2,
+};
+
+struct rmi_function {
+       unsigned page;                  /* page of the function */
+       u16 query_base_addr;            /* base address for queries */
+       u16 command_base_addr;          /* base address for commands */
+       u16 control_base_addr;          /* base address for controls */
+       u16 data_base_addr;             /* base address for datas */
+       unsigned int interrupt_base;    /* cross-function interrupt number
+                                        * (uniq in the device)*/
+       unsigned int interrupt_count;   /* number of interrupts */
+       unsigned int report_size;       /* size of a report */
+       unsigned long irq_mask;         /* mask of the interrupts
+                                        * (to be applied against ATTN IRQ) */
+};
+
+/**
+ * struct rmi_data - stores information for hid communication
+ *
+ * @page_mutex: Locks current page to avoid changing pages in unexpected ways.
+ * @page: Keeps track of the current virtual page
+ *
+ * @wait: Used for waiting for read data
+ *
+ * @writeReport: output buffer when writing RMI registers
+ * @readReport: input buffer when reading RMI registers
+ *
+ * @input_report_size: size of an input report (advertised by HID)
+ * @output_report_size: size of an output report (advertised by HID)
+ *
+ * @flags: flags for the current device (started, reading, etc...)
+ *
+ * @f11: placeholder of internal RMI function F11 description
+ * @f30: placeholder of internal RMI function F30 description
+ *
+ * @max_fingers: maximum finger count reported by the device
+ * @max_x: maximum x value reported by the device
+ * @max_y: maximum y value reported by the device
+ *
+ * @gpio_led_count: count of GPIOs + LEDs reported by F30
+ * @button_count: actual physical buttons count
+ * @button_mask: button mask used to decode GPIO ATTN reports
+ * @button_state_mask: pull state of the buttons
+ *
+ * @input: pointer to the kernel input device
+ *
+ * @reset_work: worker which will be called in case of a mouse report
+ * @hdev: pointer to the struct hid_device
+ */
+struct rmi_data {
+       struct mutex page_mutex;
+       int page;
+
+       wait_queue_head_t wait;
+
+       u8 *writeReport;
+       u8 *readReport;
+
+       int input_report_size;
+       int output_report_size;
+
+       unsigned long flags;
+
+       struct rmi_function f11;
+       struct rmi_function f30;
+
+       unsigned int max_fingers;
+       unsigned int max_x;
+       unsigned int max_y;
+       unsigned int x_size_mm;
+       unsigned int y_size_mm;
+
+       unsigned int gpio_led_count;
+       unsigned int button_count;
+       unsigned long button_mask;
+       unsigned long button_state_mask;
+
+       struct input_dev *input;
+
+       struct work_struct reset_work;
+       struct hid_device *hdev;
+};
+
+#define RMI_PAGE(addr) (((addr) >> 8) & 0xff)
+
+static int rmi_write_report(struct hid_device *hdev, u8 *report, int len);
+
+/**
+ * rmi_set_page - Set RMI page
+ * @hdev: The pointer to the hid_device struct
+ * @page: The new page address.
+ *
+ * RMI devices have 16-bit addressing, but some of the physical
+ * implementations (like SMBus) only have 8-bit addressing. So RMI implements
+ * a page address at 0xff of every page so we can reliable page addresses
+ * every 256 registers.
+ *
+ * The page_mutex lock must be held when this function is entered.
+ *
+ * Returns zero on success, non-zero on failure.
+ */
+static int rmi_set_page(struct hid_device *hdev, u8 page)
+{
+       struct rmi_data *data = hid_get_drvdata(hdev);
+       int retval;
+
+       data->writeReport[0] = RMI_WRITE_REPORT_ID;
+       data->writeReport[1] = 1;
+       data->writeReport[2] = 0xFF;
+       data->writeReport[4] = page;
+
+       retval = rmi_write_report(hdev, data->writeReport,
+                       data->output_report_size);
+       if (retval != data->output_report_size) {
+               dev_err(&hdev->dev,
+                       "%s: set page failed: %d.", __func__, retval);
+               return retval;
+       }
+
+       data->page = page;
+       return 0;
+}
+
+static int rmi_set_mode(struct hid_device *hdev, u8 mode)
+{
+       int ret;
+       u8 txbuf[2] = {RMI_SET_RMI_MODE_REPORT_ID, mode};
+
+       ret = hid_hw_raw_request(hdev, RMI_SET_RMI_MODE_REPORT_ID, txbuf,
+                       sizeof(txbuf), HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+       if (ret < 0) {
+               dev_err(&hdev->dev, "unable to set rmi mode to %d (%d)\n", mode,
+                       ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int rmi_write_report(struct hid_device *hdev, u8 *report, int len)
+{
+       int ret;
+
+       ret = hid_hw_output_report(hdev, (void *)report, len);
+       if (ret < 0) {
+               dev_err(&hdev->dev, "failed to write hid report (%d)\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int rmi_read_block(struct hid_device *hdev, u16 addr, void *buf,
+               const int len)
+{
+       struct rmi_data *data = hid_get_drvdata(hdev);
+       int ret;
+       int bytes_read;
+       int bytes_needed;
+       int retries;
+       int read_input_count;
+
+       mutex_lock(&data->page_mutex);
+
+       if (RMI_PAGE(addr) != data->page) {
+               ret = rmi_set_page(hdev, RMI_PAGE(addr));
+               if (ret < 0)
+                       goto exit;
+       }
+
+       for (retries = 5; retries > 0; retries--) {
+               data->writeReport[0] = RMI_READ_ADDR_REPORT_ID;
+               data->writeReport[1] = 0; /* old 1 byte read count */
+               data->writeReport[2] = addr & 0xFF;
+               data->writeReport[3] = (addr >> 8) & 0xFF;
+               data->writeReport[4] = len  & 0xFF;
+               data->writeReport[5] = (len >> 8) & 0xFF;
+
+               set_bit(RMI_READ_REQUEST_PENDING, &data->flags);
+
+               ret = rmi_write_report(hdev, data->writeReport,
+                                               data->output_report_size);
+               if (ret != data->output_report_size) {
+                       clear_bit(RMI_READ_REQUEST_PENDING, &data->flags);
+                       dev_err(&hdev->dev,
+                               "failed to write request output report (%d)\n",
+                               ret);
+                       goto exit;
+               }
+
+               bytes_read = 0;
+               bytes_needed = len;
+               while (bytes_read < len) {
+                       if (!wait_event_timeout(data->wait,
+                               test_bit(RMI_READ_DATA_PENDING, &data->flags),
+                                       msecs_to_jiffies(1000))) {
+                               hid_warn(hdev, "%s: timeout elapsed\n",
+                                        __func__);
+                               ret = -EAGAIN;
+                               break;
+                       }
+
+                       read_input_count = data->readReport[1];
+                       memcpy(buf + bytes_read, &data->readReport[2],
+                               read_input_count < bytes_needed ?
+                                       read_input_count : bytes_needed);
+
+                       bytes_read += read_input_count;
+                       bytes_needed -= read_input_count;
+                       clear_bit(RMI_READ_DATA_PENDING, &data->flags);
+               }
+
+               if (ret >= 0) {
+                       ret = 0;
+                       break;
+               }
+       }
+
+exit:
+       clear_bit(RMI_READ_REQUEST_PENDING, &data->flags);
+       mutex_unlock(&data->page_mutex);
+       return ret;
+}
+
+static inline int rmi_read(struct hid_device *hdev, u16 addr, void *buf)
+{
+       return rmi_read_block(hdev, addr, buf, 1);
+}
+
+static void rmi_f11_process_touch(struct rmi_data *hdata, int slot,
+               u8 finger_state, u8 *touch_data)
+{
+       int x, y, wx, wy;
+       int wide, major, minor;
+       int z;
+
+       input_mt_slot(hdata->input, slot);
+       input_mt_report_slot_state(hdata->input, MT_TOOL_FINGER,
+                       finger_state == 0x01);
+       if (finger_state == 0x01) {
+               x = (touch_data[0] << 4) | (touch_data[2] & 0x0F);
+               y = (touch_data[1] << 4) | (touch_data[2] >> 4);
+               wx = touch_data[3] & 0x0F;
+               wy = touch_data[3] >> 4;
+               wide = (wx > wy);
+               major = max(wx, wy);
+               minor = min(wx, wy);
+               z = touch_data[4];
+
+               /* y is inverted */
+               y = hdata->max_y - y;
+
+               input_event(hdata->input, EV_ABS, ABS_MT_POSITION_X, x);
+               input_event(hdata->input, EV_ABS, ABS_MT_POSITION_Y, y);
+               input_event(hdata->input, EV_ABS, ABS_MT_ORIENTATION, wide);
+               input_event(hdata->input, EV_ABS, ABS_MT_PRESSURE, z);
+               input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
+               input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
+       }
+}
+
+static void rmi_reset_work(struct work_struct *work)
+{
+       struct rmi_data *hdata = container_of(work, struct rmi_data,
+                                               reset_work);
+
+       /* switch the device to RMI if we receive a generic mouse report */
+       rmi_set_mode(hdata->hdev, RMI_MODE_ATTN_REPORTS);
+}
+
+static inline int rmi_schedule_reset(struct hid_device *hdev)
+{
+       struct rmi_data *hdata = hid_get_drvdata(hdev);
+       return schedule_work(&hdata->reset_work);
+}
+
+static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data,
+               int size)
+{
+       struct rmi_data *hdata = hid_get_drvdata(hdev);
+       int offset;
+       int i;
+
+       if (size < hdata->f11.report_size)
+               return 0;
+
+       if (!(irq & hdata->f11.irq_mask))
+               return 0;
+
+       offset = (hdata->max_fingers >> 2) + 1;
+       for (i = 0; i < hdata->max_fingers; i++) {
+               int fs_byte_position = i >> 2;
+               int fs_bit_position = (i & 0x3) << 1;
+               int finger_state = (data[fs_byte_position] >> fs_bit_position) &
+                                       0x03;
+
+               rmi_f11_process_touch(hdata, i, finger_state,
+                               &data[offset + 5 * i]);
+       }
+       input_mt_sync_frame(hdata->input);
+       input_sync(hdata->input);
+       return hdata->f11.report_size;
+}
+
+static int rmi_f30_input_event(struct hid_device *hdev, u8 irq, u8 *data,
+               int size)
+{
+       struct rmi_data *hdata = hid_get_drvdata(hdev);
+       int i;
+       int button = 0;
+       bool value;
+
+       if (!(irq & hdata->f30.irq_mask))
+               return 0;
+
+       for (i = 0; i < hdata->gpio_led_count; i++) {
+               if (test_bit(i, &hdata->button_mask)) {
+                       value = (data[i / 8] >> (i & 0x07)) & BIT(0);
+                       if (test_bit(i, &hdata->button_state_mask))
+                               value = !value;
+                       input_event(hdata->input, EV_KEY, BTN_LEFT + button++,
+                                       value);
+               }
+       }
+       return hdata->f30.report_size;
+}
+
+static int rmi_input_event(struct hid_device *hdev, u8 *data, int size)
+{
+       struct rmi_data *hdata = hid_get_drvdata(hdev);
+       unsigned long irq_mask = 0;
+       unsigned index = 2;
+
+       if (!(test_bit(RMI_STARTED, &hdata->flags)))
+               return 0;
+
+       irq_mask |= hdata->f11.irq_mask;
+       irq_mask |= hdata->f30.irq_mask;
+
+       if (data[1] & ~irq_mask)
+               hid_warn(hdev, "unknown intr source:%02lx %s:%d\n",
+                       data[1] & ~irq_mask, __FILE__, __LINE__);
+
+       if (hdata->f11.interrupt_base < hdata->f30.interrupt_base) {
+               index += rmi_f11_input_event(hdev, data[1], &data[index],
+                               size - index);
+               index += rmi_f30_input_event(hdev, data[1], &data[index],
+                               size - index);
+       } else {
+               index += rmi_f30_input_event(hdev, data[1], &data[index],
+                               size - index);
+               index += rmi_f11_input_event(hdev, data[1], &data[index],
+                               size - index);
+       }
+
+       return 1;
+}
+
+static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size)
+{
+       struct rmi_data *hdata = hid_get_drvdata(hdev);
+
+       if (!test_bit(RMI_READ_REQUEST_PENDING, &hdata->flags)) {
+               hid_err(hdev, "no read request pending\n");
+               return 0;
+       }
+
+       memcpy(hdata->readReport, data, size < hdata->input_report_size ?
+                       size : hdata->input_report_size);
+       set_bit(RMI_READ_DATA_PENDING, &hdata->flags);
+       wake_up(&hdata->wait);
+
+       return 1;
+}
+
+static int rmi_raw_event(struct hid_device *hdev,
+               struct hid_report *report, u8 *data, int size)
+{
+       switch (data[0]) {
+       case RMI_READ_DATA_REPORT_ID:
+               return rmi_read_data_event(hdev, data, size);
+       case RMI_ATTN_REPORT_ID:
+               return rmi_input_event(hdev, data, size);
+       case RMI_MOUSE_REPORT_ID:
+               rmi_schedule_reset(hdev);
+               break;
+       }
+
+       return 0;
+}
+
+static int rmi_post_reset(struct hid_device *hdev)
+{
+       return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
+}
+
+static int rmi_post_resume(struct hid_device *hdev)
+{
+       return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
+}
+
+#define RMI4_MAX_PAGE 0xff
+#define RMI4_PAGE_SIZE 0x0100
+
+#define PDT_START_SCAN_LOCATION 0x00e9
+#define PDT_END_SCAN_LOCATION  0x0005
+#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
+
+struct pdt_entry {
+       u8 query_base_addr:8;
+       u8 command_base_addr:8;
+       u8 control_base_addr:8;
+       u8 data_base_addr:8;
+       u8 interrupt_source_count:3;
+       u8 bits3and4:2;
+       u8 function_version:2;
+       u8 bit7:1;
+       u8 function_number:8;
+} __attribute__((__packed__));
+
+static inline unsigned long rmi_gen_mask(unsigned irq_base, unsigned irq_count)
+{
+       return GENMASK(irq_count + irq_base - 1, irq_base);
+}
+
+static void rmi_register_function(struct rmi_data *data,
+       struct pdt_entry *pdt_entry, int page, unsigned interrupt_count)
+{
+       struct rmi_function *f = NULL;
+       u16 page_base = page << 8;
+
+       switch (pdt_entry->function_number) {
+       case 0x11:
+               f = &data->f11;
+               break;
+       case 0x30:
+               f = &data->f30;
+               break;
+       }
+
+       if (f) {
+               f->page = page;
+               f->query_base_addr = page_base | pdt_entry->query_base_addr;
+               f->command_base_addr = page_base | pdt_entry->command_base_addr;
+               f->control_base_addr = page_base | pdt_entry->control_base_addr;
+               f->data_base_addr = page_base | pdt_entry->data_base_addr;
+               f->interrupt_base = interrupt_count;
+               f->interrupt_count = pdt_entry->interrupt_source_count;
+               f->irq_mask = rmi_gen_mask(f->interrupt_base,
+                                               f->interrupt_count);
+       }
+}
+
+static int rmi_scan_pdt(struct hid_device *hdev)
+{
+       struct rmi_data *data = hid_get_drvdata(hdev);
+       struct pdt_entry entry;
+       int page;
+       bool page_has_function;
+       int i;
+       int retval;
+       int interrupt = 0;
+       u16 page_start, pdt_start , pdt_end;
+
+       hid_info(hdev, "Scanning PDT...\n");
+
+       for (page = 0; (page <= RMI4_MAX_PAGE); page++) {
+               page_start = RMI4_PAGE_SIZE * page;
+               pdt_start = page_start + PDT_START_SCAN_LOCATION;
+               pdt_end = page_start + PDT_END_SCAN_LOCATION;
+
+               page_has_function = false;
+               for (i = pdt_start; i >= pdt_end; i -= sizeof(entry)) {
+                       retval = rmi_read_block(hdev, i, &entry, sizeof(entry));
+                       if (retval) {
+                               hid_err(hdev,
+                                       "Read of PDT entry at %#06x failed.\n",
+                                       i);
+                               goto error_exit;
+                       }
+
+                       if (RMI4_END_OF_PDT(entry.function_number))
+                               break;
+
+                       page_has_function = true;
+
+                       hid_info(hdev, "Found F%02X on page %#04x\n",
+                                       entry.function_number, page);
+
+                       rmi_register_function(data, &entry, page, interrupt);
+                       interrupt += entry.interrupt_source_count;
+               }
+
+               if (!page_has_function)
+                       break;
+       }
+
+       hid_info(hdev, "%s: Done with PDT scan.\n", __func__);
+       retval = 0;
+
+error_exit:
+       return retval;
+}
+
+static int rmi_populate_f11(struct hid_device *hdev)
+{
+       struct rmi_data *data = hid_get_drvdata(hdev);
+       u8 buf[20];
+       int ret;
+       bool has_query9;
+       bool has_query10;
+       bool has_query11;
+       bool has_query12;
+       bool has_physical_props;
+       unsigned x_size, y_size;
+       u16 query12_offset;
+
+       if (!data->f11.query_base_addr) {
+               hid_err(hdev, "No 2D sensor found, giving up.\n");
+               return -ENODEV;
+       }
+
+       /* query 0 contains some useful information */
+       ret = rmi_read(hdev, data->f11.query_base_addr, buf);
+       if (ret) {
+               hid_err(hdev, "can not get query 0: %d.\n", ret);
+               return ret;
+       }
+       has_query9 = !!(buf[0] & BIT(3));
+       has_query11 = !!(buf[0] & BIT(4));
+       has_query12 = !!(buf[0] & BIT(5));
+
+       /* query 1 to get the max number of fingers */
+       ret = rmi_read(hdev, data->f11.query_base_addr + 1, buf);
+       if (ret) {
+               hid_err(hdev, "can not get NumberOfFingers: %d.\n", ret);
+               return ret;
+       }
+       data->max_fingers = (buf[0] & 0x07) + 1;
+       if (data->max_fingers > 5)
+               data->max_fingers = 10;
+
+       data->f11.report_size = data->max_fingers * 5 +
+                               DIV_ROUND_UP(data->max_fingers, 4);
+
+       if (!(buf[0] & BIT(4))) {
+               hid_err(hdev, "No absolute events, giving up.\n");
+               return -ENODEV;
+       }
+
+       /* query 8 to find out if query 10 exists */
+       ret = rmi_read(hdev, data->f11.query_base_addr + 8, buf);
+       if (ret) {
+               hid_err(hdev, "can not read gesture information: %d.\n", ret);
+               return ret;
+       }
+       has_query10 = !!(buf[0] & BIT(2));
+
+       /*
+        * At least 8 queries are guaranteed to be present in F11
+        * +1 for query12.
+        */
+       query12_offset = 9;
+
+       if (has_query9)
+               ++query12_offset;
+
+       if (has_query10)
+               ++query12_offset;
+
+       if (has_query11)
+               ++query12_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);
+               if (ret) {
+                       hid_err(hdev, "can not get query 12: %d.\n", ret);
+                       return ret;
+               }
+               has_physical_props = !!(buf[0] & BIT(5));
+
+               if (has_physical_props) {
+                       ret = rmi_read_block(hdev,
+                                       data->f11.query_base_addr
+                                               + query12_offset + 1, buf, 4);
+                       if (ret) {
+                               hid_err(hdev, "can not read query 15-18: %d.\n",
+                                       ret);
+                               return ret;
+                       }
+
+                       x_size = buf[0] | (buf[1] << 8);
+                       y_size = buf[2] | (buf[3] << 8);
+
+                       data->x_size_mm = DIV_ROUND_CLOSEST(x_size, 10);
+                       data->y_size_mm = DIV_ROUND_CLOSEST(y_size, 10);
+
+                       hid_info(hdev, "%s: size in mm: %d x %d\n",
+                                __func__, data->x_size_mm, data->y_size_mm);
+               }
+       }
+
+       /*
+        * retrieve the ctrl registers
+        * the ctrl register has a size of 20 but a fw bug split it into 16 + 4,
+        * and there is no way to know if the first 20 bytes are here or not.
+        * We use only the first 10 bytes, so get only them.
+        */
+       ret = rmi_read_block(hdev, data->f11.control_base_addr, buf, 10);
+       if (ret) {
+               hid_err(hdev, "can not read ctrl block of size 10: %d.\n", ret);
+               return ret;
+       }
+
+       data->max_x = buf[6] | (buf[7] << 8);
+       data->max_y = buf[8] | (buf[9] << 8);
+
+       return 0;
+}
+
+static int rmi_populate_f30(struct hid_device *hdev)
+{
+       struct rmi_data *data = hid_get_drvdata(hdev);
+       u8 buf[20];
+       int ret;
+       bool has_gpio, has_led;
+       unsigned bytes_per_ctrl;
+       u8 ctrl2_addr;
+       int ctrl2_3_length;
+       int i;
+
+       /* function F30 is for physical buttons */
+       if (!data->f30.query_base_addr) {
+               hid_err(hdev, "No GPIO/LEDs found, giving up.\n");
+               return -ENODEV;
+       }
+
+       ret = rmi_read_block(hdev, data->f30.query_base_addr, buf, 2);
+       if (ret) {
+               hid_err(hdev, "can not get F30 query registers: %d.\n", ret);
+               return ret;
+       }
+
+       has_gpio = !!(buf[0] & BIT(3));
+       has_led = !!(buf[0] & BIT(2));
+       data->gpio_led_count = buf[1] & 0x1f;
+
+       /* retrieve ctrl 2 & 3 registers */
+       bytes_per_ctrl = (data->gpio_led_count + 7) / 8;
+       /* Ctrl0 is present only if both has_gpio and has_led are set*/
+       ctrl2_addr = (has_gpio && has_led) ? bytes_per_ctrl : 0;
+       /* Ctrl1 is always be present */
+       ctrl2_addr += bytes_per_ctrl;
+       ctrl2_3_length = 2 * bytes_per_ctrl;
+
+       data->f30.report_size = bytes_per_ctrl;
+
+       ret = rmi_read_block(hdev, data->f30.control_base_addr + ctrl2_addr,
+                               buf, ctrl2_3_length);
+       if (ret) {
+               hid_err(hdev, "can not read ctrl 2&3 block of size %d: %d.\n",
+                       ctrl2_3_length, ret);
+               return ret;
+       }
+
+       for (i = 0; i < data->gpio_led_count; i++) {
+               int byte_position = i >> 3;
+               int bit_position = i & 0x07;
+               u8 dir_byte = buf[byte_position];
+               u8 data_byte = buf[byte_position + bytes_per_ctrl];
+               bool dir = (dir_byte >> bit_position) & BIT(0);
+               bool dat = (data_byte >> bit_position) & BIT(0);
+
+               if (dir == 0) {
+                       /* input mode */
+                       if (dat) {
+                               /* actual buttons have pull up resistor */
+                               data->button_count++;
+                               set_bit(i, &data->button_mask);
+                               set_bit(i, &data->button_state_mask);
+                       }
+               }
+
+       }
+
+       return 0;
+}
+
+static int rmi_populate(struct hid_device *hdev)
+{
+       int ret;
+
+       ret = rmi_scan_pdt(hdev);
+       if (ret) {
+               hid_err(hdev, "PDT scan failed with code %d.\n", ret);
+               return ret;
+       }
+
+       ret = rmi_populate_f11(hdev);
+       if (ret) {
+               hid_err(hdev, "Error while initializing F11 (%d).\n", ret);
+               return ret;
+       }
+
+       ret = rmi_populate_f30(hdev);
+       if (ret)
+               hid_warn(hdev, "Error while initializing F30 (%d).\n", ret);
+
+       return 0;
+}
+
+static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi)
+{
+       struct rmi_data *data = hid_get_drvdata(hdev);
+       struct input_dev *input = hi->input;
+       int ret;
+       int res_x, res_y, i;
+
+       data->input = input;
+
+       hid_dbg(hdev, "Opening low level driver\n");
+       ret = hid_hw_open(hdev);
+       if (ret)
+               return;
+
+       /* Allow incoming hid reports */
+       hid_device_io_start(hdev);
+
+       ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
+       if (ret < 0) {
+               dev_err(&hdev->dev, "failed to set rmi mode\n");
+               goto exit;
+       }
+
+       ret = rmi_set_page(hdev, 0);
+       if (ret < 0) {
+               dev_err(&hdev->dev, "failed to set page select to 0.\n");
+               goto exit;
+       }
+
+       ret = rmi_populate(hdev);
+       if (ret)
+               goto exit;
+
+       __set_bit(EV_ABS, input->evbit);
+       input_set_abs_params(input, ABS_MT_POSITION_X, 1, data->max_x, 0, 0);
+       input_set_abs_params(input, ABS_MT_POSITION_Y, 1, data->max_y, 0, 0);
+
+       if (data->x_size_mm && data->y_size_mm) {
+               res_x = (data->max_x - 1) / data->x_size_mm;
+               res_y = (data->max_y - 1) / data->y_size_mm;
+
+               input_abs_set_res(input, ABS_MT_POSITION_X, res_x);
+               input_abs_set_res(input, ABS_MT_POSITION_Y, res_y);
+       }
+
+       input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+       input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0);
+       input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0);
+       input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0);
+
+       input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER);
+
+       if (data->button_count) {
+               __set_bit(EV_KEY, input->evbit);
+               for (i = 0; i < data->button_count; i++)
+                       __set_bit(BTN_LEFT + i, input->keybit);
+
+               if (data->button_count == 1)
+                       __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+       }
+
+       set_bit(RMI_STARTED, &data->flags);
+
+exit:
+       hid_device_io_stop(hdev);
+       hid_hw_close(hdev);
+}
+
+static int rmi_input_mapping(struct hid_device *hdev,
+               struct hid_input *hi, struct hid_field *field,
+               struct hid_usage *usage, unsigned long **bit, int *max)
+{
+       /* we want to make HID ignore the advertised HID collection */
+       return -1;
+}
+
+static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       struct rmi_data *data = NULL;
+       int ret;
+       size_t alloc_size;
+
+       data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       INIT_WORK(&data->reset_work, rmi_reset_work);
+       data->hdev = hdev;
+
+       hid_set_drvdata(hdev, data);
+
+       hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "parse failed\n");
+               return ret;
+       }
+
+       data->input_report_size = (hdev->report_enum[HID_INPUT_REPORT]
+               .report_id_hash[RMI_ATTN_REPORT_ID]->size >> 3)
+               + 1 /* report id */;
+       data->output_report_size = (hdev->report_enum[HID_OUTPUT_REPORT]
+               .report_id_hash[RMI_WRITE_REPORT_ID]->size >> 3)
+               + 1 /* report id */;
+
+       alloc_size = data->output_report_size + data->input_report_size;
+
+       data->writeReport = devm_kzalloc(&hdev->dev, alloc_size, GFP_KERNEL);
+       if (!data->writeReport) {
+               ret = -ENOMEM;
+               return ret;
+       }
+
+       data->readReport = data->writeReport + data->output_report_size;
+
+       init_waitqueue_head(&data->wait);
+
+       mutex_init(&data->page_mutex);
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               hid_err(hdev, "hw start failed\n");
+               return ret;
+       }
+
+       if (!test_bit(RMI_STARTED, &data->flags)) {
+               hid_hw_stop(hdev);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void rmi_remove(struct hid_device *hdev)
+{
+       struct rmi_data *hdata = hid_get_drvdata(hdev);
+
+       clear_bit(RMI_STARTED, &hdata->flags);
+
+       hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id rmi_id[] = {
+       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_RMI, HID_ANY_ID, HID_ANY_ID) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, rmi_id);
+
+static struct hid_driver rmi_driver = {
+       .name = "hid-rmi",
+       .id_table               = rmi_id,
+       .probe                  = rmi_probe,
+       .remove                 = rmi_remove,
+       .raw_event              = rmi_raw_event,
+       .input_mapping          = rmi_input_mapping,
+       .input_configured       = rmi_input_configured,
+#ifdef CONFIG_PM
+       .resume                 = rmi_post_resume,
+       .reset_resume           = rmi_post_reset,
+#endif
+};
+
+module_hid_driver(rmi_driver);
+
+MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>");
+MODULE_DESCRIPTION("RMI HID driver");
+MODULE_LICENSE("GPL");
index 37961c7..69cca14 100644 (file)
@@ -1,10 +1,17 @@
 /*
- *  HID driver for Saitek devices, currently only the PS1000 (USB gamepad).
+ *  HID driver for Saitek devices.
+ *
+ *  PS1000 (USB gamepad):
  *  Fixes the HID report descriptor by removing a non-existent axis and
  *  clearing the constant bit on the input reports for buttons and d-pad.
  *  (This module is based on "hid-ortek".)
- *
  *  Copyright (c) 2012 Andreas Hübner
+ *
+ *  R.A.T.7, 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.
+ *
  */
 
 /*
 
 #include "hid-ids.h"
 
+#define SAITEK_FIX_PS1000      0x0001
+#define SAITEK_RELEASE_MODE_RAT7       0x0002
+#define SAITEK_RELEASE_MODE_MMO7       0x0004
+
+struct saitek_sc {
+       unsigned long quirks;
+       int mode;
+};
+
+static int saitek_probe(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       unsigned long quirks = id->driver_data;
+       struct saitek_sc *ssc;
+       int ret;
+
+       ssc = devm_kzalloc(&hdev->dev, sizeof(*ssc), GFP_KERNEL);
+       if (ssc == NULL) {
+               hid_err(hdev, "can't alloc saitek descriptor\n");
+               return -ENOMEM;
+       }
+
+       ssc->quirks = quirks;
+       ssc->mode = -1;
+
+       hid_set_drvdata(hdev, ssc);
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "parse failed\n");
+               return ret;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               hid_err(hdev, "hw start failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
 static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                unsigned int *rsize)
 {
-       if (*rsize == 137 && rdesc[20] == 0x09 && rdesc[21] == 0x33
-                       && rdesc[94] == 0x81 && rdesc[95] == 0x03
-                       && rdesc[110] == 0x81 && rdesc[111] == 0x03) {
+       struct saitek_sc *ssc = hid_get_drvdata(hdev);
+
+       if ((ssc->quirks & SAITEK_FIX_PS1000) && *rsize == 137 &&
+                       rdesc[20] == 0x09 && rdesc[21] == 0x33 &&
+                       rdesc[94] == 0x81 && rdesc[95] == 0x03 &&
+                       rdesc[110] == 0x81 && rdesc[111] == 0x03) {
 
                hid_info(hdev, "Fixing up Saitek PS1000 report descriptor\n");
 
@@ -42,8 +94,93 @@ static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
        return rdesc;
 }
 
+static int saitek_raw_event(struct hid_device *hdev,
+               struct hid_report *report, u8 *raw_data, int size)
+{
+       struct saitek_sc *ssc = hid_get_drvdata(hdev);
+
+       if (ssc->quirks & SAITEK_RELEASE_MODE_RAT7 && size == 7) {
+               /* R.A.T.7 uses bits 13, 14, 15 for the mode */
+               int mode = -1;
+               if (raw_data[1] & 0x01)
+                       mode = 0;
+               else if (raw_data[1] & 0x02)
+                       mode = 1;
+               else if (raw_data[1] & 0x04)
+                       mode = 2;
+
+               /* clear mode bits */
+               raw_data[1] &= ~0x07;
+
+               if (mode != ssc->mode) {
+                       hid_dbg(hdev, "entered mode %d\n", mode);
+                       if (ssc->mode != -1) {
+                               /* use bit 13 as the mode button */
+                               raw_data[1] |= 0x04;
+                       }
+                       ssc->mode = mode;
+               }
+       } else if (ssc->quirks & SAITEK_RELEASE_MODE_MMO7 && size == 8) {
+
+               /* M.M.O.7 uses bits 8, 22, 23 for the mode */
+               int mode = -1;
+               if (raw_data[1] & 0x80)
+                       mode = 0;
+               else if (raw_data[2] & 0x01)
+                       mode = 1;
+               else if (raw_data[2] & 0x02)
+                       mode = 2;
+
+               /* clear mode bits */
+               raw_data[1] &= ~0x80;
+               raw_data[2] &= ~0x03;
+
+               if (mode != ssc->mode) {
+                       hid_dbg(hdev, "entered mode %d\n", mode);
+                       if (ssc->mode != -1) {
+                               /* use bit 8 as the mode button, bits 22
+                                * and 23 do not represent buttons
+                                * according to the HID report descriptor
+                                */
+                               raw_data[1] |= 0x80;
+                       }
+                       ssc->mode = mode;
+               }
+       }
+
+       return 0;
+}
+
+static int saitek_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       struct saitek_sc *ssc = hid_get_drvdata(hdev);
+       struct input_dev *input = field->hidinput->input;
+
+       if (usage->type == EV_KEY && value &&
+                       (((ssc->quirks & SAITEK_RELEASE_MODE_RAT7) &&
+                         usage->code - BTN_MOUSE == 10) ||
+                       ((ssc->quirks & SAITEK_RELEASE_MODE_MMO7) &&
+                        usage->code - BTN_MOUSE == 15))) {
+
+               input_report_key(input, usage->code, 1);
+
+               /* report missing release event */
+               input_report_key(input, usage->code, 0);
+
+               return 1;
+       }
+
+       return 0;
+}
+
 static const struct hid_device_id saitek_devices[] = {
-       { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000)},
+       { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000),
+               .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_SAITEK, USB_DEVICE_ID_SAITEK_MMO7),
+               .driver_data = SAITEK_RELEASE_MODE_MMO7 },
        { }
 };
 
@@ -52,7 +189,10 @@ MODULE_DEVICE_TABLE(hid, saitek_devices);
 static struct hid_driver saitek_driver = {
        .name = "saitek",
        .id_table = saitek_devices,
-       .report_fixup = saitek_report_fixup
+       .probe = saitek_probe,
+       .report_fixup = saitek_report_fixup,
+       .raw_event = saitek_raw_event,
+       .event = saitek_event,
 };
 module_hid_driver(saitek_driver);
 
index be14b56..a8d5c8f 100644 (file)
@@ -705,8 +705,17 @@ static const struct hid_device_id sensor_hub_devices[] = {
        { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_1,
                        USB_DEVICE_ID_INTEL_HID_SENSOR_1),
                        .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
+                       USB_DEVICE_ID_MS_SURFACE_PRO_2),
+                       .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
+                       USB_DEVICE_ID_MS_TOUCH_COVER_2),
+                       .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
+                       USB_DEVICE_ID_MS_TYPE_COVER_2),
+                       .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
        { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0,
-                       USB_DEVICE_ID_STM_HID_SENSOR),
+                       USB_DEVICE_ID_STM_HID_SENSOR_1),
                        .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
        { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_TEXAS_INSTRUMENTS,
                        USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA),
index 908de27..2259eaa 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/power_supply.h>
 #include <linux/spinlock.h>
 #include <linux/list.h>
+#include <linux/idr.h>
 #include <linux/input/mt.h>
 
 #include "hid-ids.h"
@@ -717,8 +718,39 @@ static enum power_supply_property sony_battery_props[] = {
        POWER_SUPPLY_PROP_STATUS,
 };
 
+struct sixaxis_led {
+       __u8 time_enabled; /* the total time the led is active (0xff means forever) */
+       __u8 duty_length;  /* how long a cycle is in deciseconds (0 means "really fast") */
+       __u8 enabled;
+       __u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */
+       __u8 duty_on;  /* % of duty_length the led is on (0xff mean 100%) */
+} __packed;
+
+struct sixaxis_rumble {
+       __u8 padding;
+       __u8 right_duration; /* Right motor duration (0xff means forever) */
+       __u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
+       __u8 left_duration;    /* Left motor duration (0xff means forever) */
+       __u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
+} __packed;
+
+struct sixaxis_output_report {
+       __u8 report_id;
+       struct sixaxis_rumble rumble;
+       __u8 padding[4];
+       __u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
+       struct sixaxis_led led[4];    /* LEDx at (4 - x) */
+       struct sixaxis_led _reserved; /* LED5, not actually soldered */
+} __packed;
+
+union sixaxis_output_report_01 {
+       struct sixaxis_output_report data;
+       __u8 buf[36];
+};
+
 static spinlock_t sony_dev_list_lock;
 static LIST_HEAD(sony_device_list);
+static DEFINE_IDA(sony_device_id_allocator);
 
 struct sony_sc {
        spinlock_t lock;
@@ -728,6 +760,7 @@ struct sony_sc {
        unsigned long quirks;
        struct work_struct state_worker;
        struct power_supply battery;
+       int device_id;
 
 #ifdef CONFIG_SONY_FF
        __u8 left;
@@ -740,6 +773,8 @@ struct sony_sc {
        __u8 battery_charging;
        __u8 battery_capacity;
        __u8 led_state[MAX_LEDS];
+       __u8 led_delay_on[MAX_LEDS];
+       __u8 led_delay_off[MAX_LEDS];
        __u8 led_count;
 };
 
@@ -1048,6 +1083,52 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
                                HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
 }
 
+static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+{
+       static const __u8 sixaxis_leds[10][4] = {
+                               { 0x01, 0x00, 0x00, 0x00 },
+                               { 0x00, 0x01, 0x00, 0x00 },
+                               { 0x00, 0x00, 0x01, 0x00 },
+                               { 0x00, 0x00, 0x00, 0x01 },
+                               { 0x01, 0x00, 0x00, 0x01 },
+                               { 0x00, 0x01, 0x00, 0x01 },
+                               { 0x00, 0x00, 0x01, 0x01 },
+                               { 0x01, 0x00, 0x01, 0x01 },
+                               { 0x00, 0x01, 0x01, 0x01 },
+                               { 0x01, 0x01, 0x01, 0x01 }
+       };
+
+       BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
+
+       if (id < 0)
+               return;
+
+       id %= 10;
+       memcpy(values, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
+}
+
+static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+{
+       /* The first 4 color/index entries match what the PS4 assigns */
+       static const __u8 color_code[7][3] = {
+                       /* Blue   */    { 0x00, 0x00, 0x01 },
+                       /* Red    */    { 0x01, 0x00, 0x00 },
+                       /* Green  */    { 0x00, 0x01, 0x00 },
+                       /* Pink   */    { 0x02, 0x00, 0x01 },
+                       /* Orange */    { 0x02, 0x01, 0x00 },
+                       /* Teal   */    { 0x00, 0x01, 0x01 },
+                       /* White  */    { 0x01, 0x01, 0x01 }
+       };
+
+       BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
+
+       if (id < 0)
+               return;
+
+       id %= 7;
+       memcpy(values, color_code[id], sizeof(color_code[id]));
+}
+
 static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
 {
        struct list_head *report_list =
@@ -1066,19 +1147,18 @@ static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
        hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
 }
 
-static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count)
+static void sony_set_leds(struct sony_sc *sc, const __u8 *leds, int count)
 {
-       struct sony_sc *drv_data = hid_get_drvdata(hdev);
        int n;
 
        BUG_ON(count > MAX_LEDS);
 
-       if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) {
-               buzz_set_leds(hdev, leds);
+       if (sc->quirks & BUZZ_CONTROLLER && count == 4) {
+               buzz_set_leds(sc->hdev, leds);
        } else {
                for (n = 0; n < count; n++)
-                       drv_data->led_state[n] = leds[n];
-               schedule_work(&drv_data->state_worker);
+                       sc->led_state[n] = leds[n];
+               schedule_work(&sc->state_worker);
        }
 }
 
@@ -1090,6 +1170,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
        struct sony_sc *drv_data;
 
        int n;
+       int force_update;
 
        drv_data = hid_get_drvdata(hdev);
        if (!drv_data) {
@@ -1097,12 +1178,29 @@ static void sony_led_set_brightness(struct led_classdev *led,
                return;
        }
 
+       /*
+        * The Sixaxis on USB will override any LED settings sent to it
+        * and keep flashing all of the LEDs until the PS button is pressed.
+        * Updates, even if redundant, must be always be sent to the
+        * controller to avoid having to toggle the state of an LED just to
+        * stop the flashing later on.
+        */
+       force_update = !!(drv_data->quirks & SIXAXIS_CONTROLLER_USB);
+
        for (n = 0; n < drv_data->led_count; n++) {
-               if (led == drv_data->leds[n]) {
-                       if (value != drv_data->led_state[n]) {
-                               drv_data->led_state[n] = value;
-                               sony_set_leds(hdev, drv_data->led_state, drv_data->led_count);
-                       }
+               if (led == drv_data->leds[n] && (force_update ||
+                       (value != drv_data->led_state[n] ||
+                       drv_data->led_delay_on[n] ||
+                       drv_data->led_delay_off[n]))) {
+
+                       drv_data->led_state[n] = value;
+
+                       /* Setting the brightness stops the blinking */
+                       drv_data->led_delay_on[n] = 0;
+                       drv_data->led_delay_off[n] = 0;
+
+                       sony_set_leds(drv_data, drv_data->led_state,
+                                       drv_data->led_count);
                        break;
                }
        }
@@ -1130,63 +1228,112 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
        return LED_OFF;
 }
 
-static void sony_leds_remove(struct hid_device *hdev)
+static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
+                               unsigned long *delay_off)
 {
-       struct sony_sc *drv_data;
-       struct led_classdev *led;
+       struct device *dev = led->dev->parent;
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct sony_sc *drv_data = hid_get_drvdata(hdev);
        int n;
+       __u8 new_on, new_off;
 
-       drv_data = hid_get_drvdata(hdev);
-       BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
+       if (!drv_data) {
+               hid_err(hdev, "No device data\n");
+               return -EINVAL;
+       }
+
+       /* Max delay is 255 deciseconds or 2550 milliseconds */
+       if (*delay_on > 2550)
+               *delay_on = 2550;
+       if (*delay_off > 2550)
+               *delay_off = 2550;
+
+       /* Blink at 1 Hz if both values are zero */
+       if (!*delay_on && !*delay_off)
+               *delay_on = *delay_off = 500;
+
+       new_on = *delay_on / 10;
+       new_off = *delay_off / 10;
 
        for (n = 0; n < drv_data->led_count; n++) {
-               led = drv_data->leds[n];
-               drv_data->leds[n] = NULL;
+               if (led == drv_data->leds[n])
+                       break;
+       }
+
+       /* This LED is not registered on this device */
+       if (n >= drv_data->led_count)
+               return -EINVAL;
+
+       /* Don't schedule work if the values didn't change */
+       if (new_on != drv_data->led_delay_on[n] ||
+               new_off != drv_data->led_delay_off[n]) {
+               drv_data->led_delay_on[n] = new_on;
+               drv_data->led_delay_off[n] = new_off;
+               schedule_work(&drv_data->state_worker);
+       }
+
+       return 0;
+}
+
+static void sony_leds_remove(struct sony_sc *sc)
+{
+       struct led_classdev *led;
+       int n;
+
+       BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
+
+       for (n = 0; n < sc->led_count; n++) {
+               led = sc->leds[n];
+               sc->leds[n] = NULL;
                if (!led)
                        continue;
                led_classdev_unregister(led);
                kfree(led);
        }
 
-       drv_data->led_count = 0;
+       sc->led_count = 0;
 }
 
-static int sony_leds_init(struct hid_device *hdev)
+static int sony_leds_init(struct sony_sc *sc)
 {
-       struct sony_sc *drv_data;
+       struct hid_device *hdev = sc->hdev;
        int n, ret = 0;
-       int max_brightness;
-       int use_colors;
+       int use_ds4_names;
        struct led_classdev *led;
        size_t name_sz;
        char *name;
        size_t name_len;
        const char *name_fmt;
-       static const char * const color_str[] = { "red", "green", "blue" };
-       static const __u8 initial_values[MAX_LEDS] = { 0x00, 0x00, 0x00, 0x00 };
+       static const char * const ds4_name_str[] = { "red", "green", "blue",
+                                                 "global" };
+       __u8 initial_values[MAX_LEDS] = { 0 };
+       __u8 max_brightness[MAX_LEDS] = { 1 };
+       __u8 use_hw_blink[MAX_LEDS] = { 0 };
 
-       drv_data = hid_get_drvdata(hdev);
-       BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
+       BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
 
-       if (drv_data->quirks & BUZZ_CONTROLLER) {
-               drv_data->led_count = 4;
-               max_brightness = 1;
-               use_colors = 0;
+       if (sc->quirks & BUZZ_CONTROLLER) {
+               sc->led_count = 4;
+               use_ds4_names = 0;
                name_len = strlen("::buzz#");
                name_fmt = "%s::buzz%d";
                /* Validate expected report characteristics. */
                if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
                        return -ENODEV;
-       } else if (drv_data->quirks & DUALSHOCK4_CONTROLLER) {
-               drv_data->led_count = 3;
-               max_brightness = 255;
-               use_colors = 1;
+       } else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
+               dualshock4_set_leds_from_id(sc->device_id, initial_values);
+               initial_values[3] = 1;
+               sc->led_count = 4;
+               memset(max_brightness, 255, 3);
+               use_hw_blink[3] = 1;
+               use_ds4_names = 1;
                name_len = 0;
                name_fmt = "%s:%s";
        } else {
-               drv_data->led_count = 4;
-               max_brightness = 1;
-               use_colors = 0;
+               sixaxis_set_leds_from_id(sc->device_id, initial_values);
+               sc->led_count = 4;
+               memset(use_hw_blink, 1, 4);
+               use_ds4_names = 0;
                name_len = strlen("::sony#");
                name_fmt = "%s::sony%d";
        }
@@ -1196,14 +1343,14 @@ static int sony_leds_init(struct hid_device *hdev)
         * only relevant if the driver is loaded after somebody actively set the
         * LEDs to on
         */
-       sony_set_leds(hdev, initial_values, drv_data->led_count);
+       sony_set_leds(sc, initial_values, sc->led_count);
 
        name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
 
-       for (n = 0; n < drv_data->led_count; n++) {
+       for (n = 0; n < sc->led_count; n++) {
 
-               if (use_colors)
-                       name_sz = strlen(dev_name(&hdev->dev)) + strlen(color_str[n]) + 2;
+               if (use_ds4_names)
+                       name_sz = strlen(dev_name(&hdev->dev)) + strlen(ds4_name_str[n]) + 2;
 
                led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL);
                if (!led) {
@@ -1213,30 +1360,35 @@ static int sony_leds_init(struct hid_device *hdev)
                }
 
                name = (void *)(&led[1]);
-               if (use_colors)
-                       snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), color_str[n]);
+               if (use_ds4_names)
+                       snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev),
+                       ds4_name_str[n]);
                else
                        snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
                led->name = name;
-               led->brightness = 0;
-               led->max_brightness = max_brightness;
+               led->brightness = initial_values[n];
+               led->max_brightness = max_brightness[n];
                led->brightness_get = sony_led_get_brightness;
                led->brightness_set = sony_led_set_brightness;
 
+               if (use_hw_blink[n])
+                       led->blink_set = sony_led_blink_set;
+
+               sc->leds[n] = led;
+
                ret = led_classdev_register(&hdev->dev, led);
                if (ret) {
                        hid_err(hdev, "Failed to register LED %d\n", n);
+                       sc->leds[n] = NULL;
                        kfree(led);
                        goto error_leds;
                }
-
-               drv_data->leds[n] = led;
        }
 
        return ret;
 
 error_leds:
-       sony_leds_remove(hdev);
+       sony_leds_remove(sc);
 
        return ret;
 }
@@ -1244,29 +1396,52 @@ error_leds:
 static void sixaxis_state_worker(struct work_struct *work)
 {
        struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
-       unsigned char buf[] = {
-               0x01,
-               0x00, 0xff, 0x00, 0xff, 0x00,
-               0x00, 0x00, 0x00, 0x00, 0x00,
-               0xff, 0x27, 0x10, 0x00, 0x32,
-               0xff, 0x27, 0x10, 0x00, 0x32,
-               0xff, 0x27, 0x10, 0x00, 0x32,
-               0xff, 0x27, 0x10, 0x00, 0x32,
-               0x00, 0x00, 0x00, 0x00, 0x00
+       int n;
+       union sixaxis_output_report_01 report = {
+               .buf = {
+                       0x01,
+                       0x00, 0xff, 0x00, 0xff, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00,
+                       0xff, 0x27, 0x10, 0x00, 0x32,
+                       0xff, 0x27, 0x10, 0x00, 0x32,
+                       0xff, 0x27, 0x10, 0x00, 0x32,
+                       0xff, 0x27, 0x10, 0x00, 0x32,
+                       0x00, 0x00, 0x00, 0x00, 0x00
+               }
        };
 
 #ifdef CONFIG_SONY_FF
-       buf[3] = sc->right ? 1 : 0;
-       buf[5] = sc->left;
+       report.data.rumble.right_motor_on = sc->right ? 1 : 0;
+       report.data.rumble.left_motor_force = sc->left;
 #endif
 
-       buf[10] |= sc->led_state[0] << 1;
-       buf[10] |= sc->led_state[1] << 2;
-       buf[10] |= sc->led_state[2] << 3;
-       buf[10] |= sc->led_state[3] << 4;
+       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;
+
+       /* Set flag for all leds off, required for 3rd party INTEC controller */
+       if ((report.data.leds_bitmap & 0x1E) == 0)
+               report.data.leds_bitmap |= 0x20;
 
-       hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT,
-                       HID_REQ_SET_REPORT);
+       /*
+        * The LEDs in the report are indexed in reverse order to their
+        * corresponding light on the controller.
+        * Index 0 = LED 4, index 1 = LED 3, etc...
+        *
+        * In the case of both delay values being zero (blinking disabled) the
+        * default report values should be used or the controller LED will be
+        * always off.
+        */
+       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];
+               }
+       }
+
+       hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf,
+                       sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
 }
 
 static void dualshock4_state_worker(struct work_struct *work)
@@ -1279,7 +1454,7 @@ static void dualshock4_state_worker(struct work_struct *work)
 
        if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
                buf[0] = 0x05;
-               buf[1] = 0x03;
+               buf[1] = 0xFF;
                offset = 4;
        } else {
                buf[0] = 0x11;
@@ -1295,9 +1470,18 @@ static void dualshock4_state_worker(struct work_struct *work)
        offset += 2;
 #endif
 
-       buf[offset++] = sc->led_state[0];
-       buf[offset++] = sc->led_state[1];
-       buf[offset++] = sc->led_state[2];
+       /* LED 3 is the global control */
+       if (sc->led_state[3]) {
+               buf[offset++] = sc->led_state[0];
+               buf[offset++] = sc->led_state[1];
+               buf[offset++] = sc->led_state[2];
+       } else {
+               offset += 3;
+       }
+
+       /* If both delay values are zero the DualShock 4 disables blinking. */
+       buf[offset++] = sc->led_delay_on[3];
+       buf[offset++] = sc->led_delay_off[3];
 
        if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
                hid_hw_output_report(hdev, buf, 32);
@@ -1323,9 +1507,9 @@ static int sony_play_effect(struct input_dev *dev, void *data,
        return 0;
 }
 
-static int sony_init_ff(struct hid_device *hdev)
+static int sony_init_ff(struct sony_sc *sc)
 {
-       struct hid_input *hidinput = list_entry(hdev->inputs.next,
+       struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
                                                struct hid_input, list);
        struct input_dev *input_dev = hidinput->input;
 
@@ -1334,7 +1518,7 @@ static int sony_init_ff(struct hid_device *hdev)
 }
 
 #else
-static int sony_init_ff(struct hid_device *hdev)
+static int sony_init_ff(struct sony_sc *sc)
 {
        return 0;
 }
@@ -1384,8 +1568,6 @@ static int sony_battery_get_property(struct power_supply *psy,
 
 static int sony_battery_probe(struct sony_sc *sc)
 {
-       static atomic_t power_id_seq = ATOMIC_INIT(0);
-       unsigned long power_id;
        struct hid_device *hdev = sc->hdev;
        int ret;
 
@@ -1395,15 +1577,13 @@ static int sony_battery_probe(struct sony_sc *sc)
         */
        sc->battery_capacity = 100;
 
-       power_id = (unsigned long)atomic_inc_return(&power_id_seq);
-
        sc->battery.properties = sony_battery_props;
        sc->battery.num_properties = ARRAY_SIZE(sony_battery_props);
        sc->battery.get_property = sony_battery_get_property;
        sc->battery.type = POWER_SUPPLY_TYPE_BATTERY;
        sc->battery.use_for_apm = 0;
-       sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%lu",
-                                    power_id);
+       sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%pMR",
+                                    sc->mac_address);
        if (!sc->battery.name)
                return -ENOMEM;
 
@@ -1578,6 +1758,52 @@ static int sony_check_add(struct sony_sc *sc)
        return sony_check_add_dev_list(sc);
 }
 
+static int sony_set_device_id(struct sony_sc *sc)
+{
+       int ret;
+
+       /*
+        * Only DualShock 4 or Sixaxis controllers get an id.
+        * All others are set to -1.
+        */
+       if ((sc->quirks & SIXAXIS_CONTROLLER) ||
+           (sc->quirks & DUALSHOCK4_CONTROLLER)) {
+               ret = ida_simple_get(&sony_device_id_allocator, 0, 0,
+                                       GFP_KERNEL);
+               if (ret < 0) {
+                       sc->device_id = -1;
+                       return ret;
+               }
+               sc->device_id = ret;
+       } else {
+               sc->device_id = -1;
+       }
+
+       return 0;
+}
+
+static void sony_release_device_id(struct sony_sc *sc)
+{
+       if (sc->device_id >= 0) {
+               ida_simple_remove(&sony_device_id_allocator, sc->device_id);
+               sc->device_id = -1;
+       }
+}
+
+static inline void sony_init_work(struct sony_sc *sc,
+                                       void (*worker)(struct work_struct *))
+{
+       if (!sc->worker_initialized)
+               INIT_WORK(&sc->state_worker, worker);
+
+       sc->worker_initialized = 1;
+}
+
+static inline void sony_cancel_work_sync(struct sony_sc *sc)
+{
+       if (sc->worker_initialized)
+               cancel_work_sync(&sc->state_worker);
+}
 
 static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
@@ -1615,6 +1841,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                return ret;
        }
 
+       ret = sony_set_device_id(sc);
+       if (ret < 0) {
+               hid_err(hdev, "failed to allocate the device id\n");
+               goto err_stop;
+       }
+
        if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
                /*
                 * The Sony Sixaxis does not handle HID Output Reports on the
@@ -1629,8 +1861,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
                hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
                ret = sixaxis_set_operational_usb(hdev);
-               sc->worker_initialized = 1;
-               INIT_WORK(&sc->state_worker, sixaxis_state_worker);
+               sony_init_work(sc, sixaxis_state_worker);
        } else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
                /*
                 * The Sixaxis wants output reports sent on the ctrl endpoint
@@ -1638,8 +1869,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                 */
                hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
                ret = sixaxis_set_operational_bt(hdev);
-               sc->worker_initialized = 1;
-               INIT_WORK(&sc->state_worker, sixaxis_state_worker);
+               sony_init_work(sc, sixaxis_state_worker);
        } else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
                if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
                        /*
@@ -1661,8 +1891,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                if (ret < 0)
                        goto err_stop;
 
-               sc->worker_initialized = 1;
-               INIT_WORK(&sc->state_worker, dualshock4_state_worker);
+               sony_init_work(sc, dualshock4_state_worker);
        } else {
                ret = 0;
        }
@@ -1675,7 +1904,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                goto err_stop;
 
        if (sc->quirks & SONY_LED_SUPPORT) {
-               ret = sony_leds_init(hdev);
+               ret = sony_leds_init(sc);
                if (ret < 0)
                        goto err_stop;
        }
@@ -1694,7 +1923,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
        }
 
        if (sc->quirks & SONY_FF_SUPPORT) {
-               ret = sony_init_ff(hdev);
+               ret = sony_init_ff(sc);
                if (ret < 0)
                        goto err_close;
        }
@@ -1704,12 +1933,12 @@ err_close:
        hid_hw_close(hdev);
 err_stop:
        if (sc->quirks & SONY_LED_SUPPORT)
-               sony_leds_remove(hdev);
+               sony_leds_remove(sc);
        if (sc->quirks & SONY_BATTERY_SUPPORT)
                sony_battery_remove(sc);
-       if (sc->worker_initialized)
-               cancel_work_sync(&sc->state_worker);
+       sony_cancel_work_sync(sc);
        sony_remove_dev_list(sc);
+       sony_release_device_id(sc);
        hid_hw_stop(hdev);
        return ret;
 }
@@ -1719,18 +1948,19 @@ static void sony_remove(struct hid_device *hdev)
        struct sony_sc *sc = hid_get_drvdata(hdev);
 
        if (sc->quirks & SONY_LED_SUPPORT)
-               sony_leds_remove(hdev);
+               sony_leds_remove(sc);
 
        if (sc->quirks & SONY_BATTERY_SUPPORT) {
                hid_hw_close(hdev);
                sony_battery_remove(sc);
        }
 
-       if (sc->worker_initialized)
-               cancel_work_sync(&sc->state_worker);
+       sony_cancel_work_sync(sc);
 
        sony_remove_dev_list(sc);
 
+       sony_release_device_id(sc);
+
        hid_hw_stop(hdev);
 }
 
@@ -1775,6 +2005,22 @@ static struct hid_driver sony_driver = {
        .report_fixup  = sony_report_fixup,
        .raw_event     = sony_raw_event
 };
-module_hid_driver(sony_driver);
+
+static int __init sony_init(void)
+{
+       dbg_hid("Sony:%s\n", __func__);
+
+       return hid_register_driver(&sony_driver);
+}
+
+static void __exit sony_exit(void)
+{
+       dbg_hid("Sony:%s\n", __func__);
+
+       ida_destroy(&sony_device_id_allocator);
+       hid_unregister_driver(&sony_driver);
+}
+module_init(sony_init);
+module_exit(sony_exit);
 
 MODULE_LICENSE("GPL");
index a97c788..134be89 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * ThingM blink(1) USB RGB LED driver
  *
- * Copyright 2013 Savoir-faire Linux Inc.
+ * Copyright 2013-2014 Savoir-faire Linux Inc.
  *     Vivien Didelot <vivien.didelot@savoirfairelinux.com>
  *
  * This program is free software; you can redistribute it and/or
  */
 
 #include <linux/hid.h>
+#include <linux/hidraw.h>
 #include <linux/leds.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
 
 #include "hid-ids.h"
 
-#define BLINK1_CMD_SIZE                9
+#define REPORT_ID      1
+#define REPORT_SIZE    9
 
-#define blink1_rgb_to_r(rgb)   ((rgb & 0xFF0000) >> 16)
-#define blink1_rgb_to_g(rgb)   ((rgb & 0x00FF00) >> 8)
-#define blink1_rgb_to_b(rgb)   ((rgb & 0x0000FF) >> 0)
+/* Firmware major number of supported devices */
+#define THINGM_MAJOR_MK1       '1'
+#define THINGM_MAJOR_MK2       '2'
 
-/**
- * struct blink1_data - blink(1) device specific data
- * @hdev:              HID device.
- * @led_cdev:          LED class instance.
- * @rgb:               8-bit per channel RGB notation.
- * @fade:              fade time in hundredths of a second.
- * @brightness:                brightness coefficient.
- * @play:              play/pause in-memory patterns.
- */
-struct blink1_data {
+struct thingm_fwinfo {
+       char major;
+       unsigned numrgb;
+       unsigned first;
+};
+
+static const struct thingm_fwinfo thingm_fwinfo[] = {
+       {
+               .major = THINGM_MAJOR_MK1,
+               .numrgb = 1,
+               .first = 0,
+       }, {
+               .major = THINGM_MAJOR_MK2,
+               .numrgb = 2,
+               .first = 1,
+       }
+};
+
+/* A red, green or blue channel, part of an RGB chip */
+struct thingm_led {
+       struct thingm_rgb *rgb;
+       struct led_classdev ldev;
+       char name[32];
+};
+
+/* Basically a WS2812 5050 RGB LED chip */
+struct thingm_rgb {
+       struct thingm_device *tdev;
+       struct thingm_led red;
+       struct thingm_led green;
+       struct thingm_led blue;
+       struct work_struct work;
+       u8 num;
+};
+
+struct thingm_device {
        struct hid_device *hdev;
-       struct led_classdev led_cdev;
-       u32 rgb;
-       u16 fade;
-       u8 brightness;
-       bool play;
+       struct {
+               char major;
+               char minor;
+       } version;
+       const struct thingm_fwinfo *fwinfo;
+       struct mutex lock;
+       struct thingm_rgb *rgb;
 };
 
-static int blink1_send_command(struct blink1_data *data,
-               u8 buf[BLINK1_CMD_SIZE])
+static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
 {
        int ret;
 
-       hid_dbg(data->hdev, "command: %d%c%.2x%.2x%.2x%.2x%.2x%.2x%.2x\n",
+       hid_dbg(tdev->hdev, "-> %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n",
                        buf[0], buf[1], buf[2], buf[3], buf[4],
                        buf[5], buf[6], buf[7], buf[8]);
 
-       ret = hid_hw_raw_request(data->hdev, buf[0], buf, BLINK1_CMD_SIZE,
-                                HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+       ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
+                       HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 
        return ret < 0 ? ret : 0;
 }
 
-static int blink1_update_color(struct blink1_data *data)
+static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
 {
-       u8 buf[BLINK1_CMD_SIZE] = { 1, 'n', 0, 0, 0, 0, 0, 0, 0 };
-
-       if (data->brightness) {
-               unsigned int coef = DIV_ROUND_CLOSEST(255, data->brightness);
+       int ret;
 
-               buf[2] = DIV_ROUND_CLOSEST(blink1_rgb_to_r(data->rgb), coef);
-               buf[3] = DIV_ROUND_CLOSEST(blink1_rgb_to_g(data->rgb), coef);
-               buf[4] = DIV_ROUND_CLOSEST(blink1_rgb_to_b(data->rgb), coef);
-       }
+       ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
+                       HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+       if (ret < 0)
+               return ret;
 
-       if (data->fade) {
-               buf[1] = 'c';
-               buf[5] = (data->fade & 0xFF00) >> 8;
-               buf[6] = (data->fade & 0x00FF);
-       }
+       hid_dbg(tdev->hdev, "<- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n",
+                       buf[0], buf[1], buf[2], buf[3], buf[4],
+                       buf[5], buf[6], buf[7], buf[8]);
 
-       return blink1_send_command(data, buf);
+       return 0;
 }
 
-static void blink1_led_set(struct led_classdev *led_cdev,
-               enum led_brightness brightness)
+static int thingm_version(struct thingm_device *tdev)
 {
-       struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent);
+       u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 };
+       int err;
 
-       data->brightness = brightness;
-       if (blink1_update_color(data))
-               hid_err(data->hdev, "failed to update color\n");
-}
+       err = thingm_send(tdev, buf);
+       if (err)
+               return err;
 
-static enum led_brightness blink1_led_get(struct led_classdev *led_cdev)
-{
-       struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent);
+       err = thingm_recv(tdev, buf);
+       if (err)
+               return err;
 
-       return data->brightness;
+       tdev->version.major = buf[3];
+       tdev->version.minor = buf[4];
+
+       return 0;
 }
 
-static ssize_t blink1_show_rgb(struct device *dev,
-               struct device_attribute *attr, char *buf)
+static int thingm_write_color(struct thingm_rgb *rgb)
 {
-       struct blink1_data *data = dev_get_drvdata(dev->parent);
+       u8 buf[REPORT_SIZE] = { REPORT_ID, 'c', 0, 0, 0, 0, 0, rgb->num, 0 };
 
-       return sprintf(buf, "%.6X\n", data->rgb);
+       buf[2] = rgb->red.ldev.brightness;
+       buf[3] = rgb->green.ldev.brightness;
+       buf[4] = rgb->blue.ldev.brightness;
+
+       return thingm_send(rgb->tdev, buf);
 }
 
-static ssize_t blink1_store_rgb(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
+static void thingm_work(struct work_struct *work)
 {
-       struct blink1_data *data = dev_get_drvdata(dev->parent);
-       long unsigned int rgb;
-       int ret;
+       struct thingm_rgb *rgb = container_of(work, struct thingm_rgb, work);
 
-       ret = kstrtoul(buf, 16, &rgb);
-       if (ret)
-               return ret;
-
-       /* RGB triplet notation is 24-bit hexadecimal */
-       if (rgb > 0xFFFFFF)
-               return -EINVAL;
+       mutex_lock(&rgb->tdev->lock);
 
-       data->rgb = rgb;
-       ret = blink1_update_color(data);
+       if (thingm_write_color(rgb))
+               hid_err(rgb->tdev->hdev, "failed to write color\n");
 
-       return ret ? ret : count;
+       mutex_unlock(&rgb->tdev->lock);
 }
 
-static DEVICE_ATTR(rgb, S_IRUGO | S_IWUSR, blink1_show_rgb, blink1_store_rgb);
-
-static ssize_t blink1_show_fade(struct device *dev,
-               struct device_attribute *attr, char *buf)
+static void thingm_led_set(struct led_classdev *ldev,
+               enum led_brightness brightness)
 {
-       struct blink1_data *data = dev_get_drvdata(dev->parent);
+       struct thingm_led *led = container_of(ldev, struct thingm_led, ldev);
 
-       return sprintf(buf, "%d\n", data->fade * 10);
+       /* the ledclass has already stored the brightness value */
+       schedule_work(&led->rgb->work);
 }
 
-static ssize_t blink1_store_fade(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
+static int thingm_init_rgb(struct thingm_rgb *rgb)
 {
-       struct blink1_data *data = dev_get_drvdata(dev->parent);
-       long unsigned int fade;
-       int ret;
+       const int minor = ((struct hidraw *) rgb->tdev->hdev->hidraw)->minor;
+       int err;
+
+       /* Register the red diode */
+       snprintf(rgb->red.name, sizeof(rgb->red.name),
+                       "thingm%d:red:led%d", minor, rgb->num);
+       rgb->red.ldev.name = rgb->red.name;
+       rgb->red.ldev.max_brightness = 255;
+       rgb->red.ldev.brightness_set = thingm_led_set;
+       rgb->red.rgb = rgb;
+
+       err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->red.ldev);
+       if (err)
+               return err;
+
+       /* Register the green diode */
+       snprintf(rgb->green.name, sizeof(rgb->green.name),
+                       "thingm%d:green:led%d", minor, rgb->num);
+       rgb->green.ldev.name = rgb->green.name;
+       rgb->green.ldev.max_brightness = 255;
+       rgb->green.ldev.brightness_set = thingm_led_set;
+       rgb->green.rgb = rgb;
+
+       err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->green.ldev);
+       if (err)
+               goto unregister_red;
+
+       /* Register the blue diode */
+       snprintf(rgb->blue.name, sizeof(rgb->blue.name),
+                       "thingm%d:blue:led%d", minor, rgb->num);
+       rgb->blue.ldev.name = rgb->blue.name;
+       rgb->blue.ldev.max_brightness = 255;
+       rgb->blue.ldev.brightness_set = thingm_led_set;
+       rgb->blue.rgb = rgb;
+
+       err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->blue.ldev);
+       if (err)
+               goto unregister_green;
+
+       INIT_WORK(&rgb->work, thingm_work);
 
-       ret = kstrtoul(buf, 10, &fade);
-       if (ret)
-               return ret;
+       return 0;
 
-       /* blink(1) accepts 16-bit fade time, number of 10ms ticks */
-       fade = DIV_ROUND_CLOSEST(fade, 10);
-       if (fade > 65535)
-               return -EINVAL;
+unregister_green:
+       led_classdev_unregister(&rgb->green.ldev);
 
-       data->fade = fade;
+unregister_red:
+       led_classdev_unregister(&rgb->red.ldev);
 
-       return count;
+       return err;
 }
 
-static DEVICE_ATTR(fade, S_IRUGO | S_IWUSR,
-               blink1_show_fade, blink1_store_fade);
-
-static ssize_t blink1_show_play(struct device *dev,
-               struct device_attribute *attr, char *buf)
+static void thingm_remove_rgb(struct thingm_rgb *rgb)
 {
-       struct blink1_data *data = dev_get_drvdata(dev->parent);
-
-       return sprintf(buf, "%d\n", data->play);
+       flush_work(&rgb->work);
+       led_classdev_unregister(&rgb->red.ldev);
+       led_classdev_unregister(&rgb->green.ldev);
+       led_classdev_unregister(&rgb->blue.ldev);
 }
 
-static ssize_t blink1_store_play(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
+static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
-       struct blink1_data *data = dev_get_drvdata(dev->parent);
-       u8 cmd[BLINK1_CMD_SIZE] = { 1, 'p', 0, 0, 0, 0, 0, 0, 0 };
-       long unsigned int play;
-       int ret;
+       struct thingm_device *tdev;
+       int i, err;
 
-       ret = kstrtoul(buf, 10, &play);
-       if (ret)
-               return ret;
+       tdev = devm_kzalloc(&hdev->dev, sizeof(struct thingm_device),
+                       GFP_KERNEL);
+       if (!tdev)
+               return -ENOMEM;
 
-       data->play = !!play;
-       cmd[2] = data->play;
-       ret = blink1_send_command(data, cmd);
+       tdev->hdev = hdev;
+       hid_set_drvdata(hdev, tdev);
 
-       return ret ? ret : count;
-}
-
-static DEVICE_ATTR(play, S_IRUGO | S_IWUSR,
-               blink1_show_play, blink1_store_play);
+       err = hid_parse(hdev);
+       if (err)
+               goto error;
 
-static const struct attribute_group blink1_sysfs_group = {
-       .attrs = (struct attribute *[]) {
-               &dev_attr_rgb.attr,
-               &dev_attr_fade.attr,
-               &dev_attr_play.attr,
-               NULL
-       },
-};
+       err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+       if (err)
+               goto error;
 
-static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
-{
-       struct blink1_data *data;
-       struct led_classdev *led;
-       char led_name[13];
-       int ret;
+       mutex_init(&tdev->lock);
 
-       data = devm_kzalloc(&hdev->dev, sizeof(struct blink1_data), GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
+       err = thingm_version(tdev);
+       if (err)
+               goto stop;
 
-       hid_set_drvdata(hdev, data);
-       data->hdev = hdev;
-       data->rgb = 0xFFFFFF; /* set a default white color */
+       hid_dbg(hdev, "firmware version: %c.%c\n",
+                       tdev->version.major, tdev->version.minor);
 
-       ret = hid_parse(hdev);
-       if (ret)
-               goto error;
+       for (i = 0; i < ARRAY_SIZE(thingm_fwinfo) && !tdev->fwinfo; ++i)
+               if (thingm_fwinfo[i].major == tdev->version.major)
+                       tdev->fwinfo = &thingm_fwinfo[i];
 
-       ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
-       if (ret)
-               goto error;
+       if (!tdev->fwinfo) {
+               hid_err(hdev, "unsupported firmware %c\n", tdev->version.major);
+               goto stop;
+       }
 
-       /* blink(1) serial numbers range is 0x1A001000 to 0x1A002FFF */
-       led = &data->led_cdev;
-       snprintf(led_name, sizeof(led_name), "blink1::%s", hdev->uniq + 4);
-       led->name = led_name;
-       led->brightness_set = blink1_led_set;
-       led->brightness_get = blink1_led_get;
-       ret = led_classdev_register(&hdev->dev, led);
-       if (ret)
+       tdev->rgb = devm_kzalloc(&hdev->dev,
+                       sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb,
+                       GFP_KERNEL);
+       if (!tdev->rgb) {
+               err = -ENOMEM;
                goto stop;
+       }
 
-       ret = sysfs_create_group(&led->dev->kobj, &blink1_sysfs_group);
-       if (ret)
-               goto remove_led;
+       for (i = 0; i < tdev->fwinfo->numrgb; ++i) {
+               struct thingm_rgb *rgb = tdev->rgb + i;
+
+               rgb->tdev = tdev;
+               rgb->num = tdev->fwinfo->first + i;
+               err = thingm_init_rgb(rgb);
+               if (err) {
+                       while (--i >= 0)
+                               thingm_remove_rgb(tdev->rgb + i);
+                       goto stop;
+               }
+       }
 
        return 0;
-
-remove_led:
-       led_classdev_unregister(led);
 stop:
        hid_hw_stop(hdev);
 error:
-       return ret;
+       return err;
 }
 
 static void thingm_remove(struct hid_device *hdev)
 {
-       struct blink1_data *data = hid_get_drvdata(hdev);
-       struct led_classdev *led = &data->led_cdev;
+       struct thingm_device *tdev = hid_get_drvdata(hdev);
+       int i;
+
+       for (i = 0; i < tdev->fwinfo->numrgb; ++i)
+               thingm_remove_rgb(tdev->rgb + i);
 
-       sysfs_remove_group(&led->dev->kobj, &blink1_sysfs_group);
-       led_classdev_unregister(led);
        hid_hw_stop(hdev);
 }
 
index b50860d..21aafc8 100644 (file)
@@ -807,34 +807,18 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
        unsigned int dsize;
        int ret;
 
-       /* Fetch the length of HID description, retrieve the 4 first bytes:
-        * bytes 0-1 -> length
-        * bytes 2-3 -> bcdVersion (has to be 1.00) */
-       ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, 4);
-
-       i2c_hid_dbg(ihid, "%s, ihid->hdesc_buffer: %4ph\n", __func__,
-                       ihid->hdesc_buffer);
-
+       /* i2c hid fetch using a fixed descriptor size (30 bytes) */
+       i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
+       ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer,
+                               sizeof(struct i2c_hid_desc));
        if (ret) {
-               dev_err(&client->dev,
-                       "unable to fetch the size of HID descriptor (ret=%d)\n",
-                       ret);
-               return -ENODEV;
-       }
-
-       dsize = le16_to_cpu(hdesc->wHIDDescLength);
-       /*
-        * the size of the HID descriptor should at least contain
-        * its size and the bcdVersion (4 bytes), and should not be greater
-        * than sizeof(struct i2c_hid_desc) as we directly fill this struct
-        * through i2c_hid_command.
-        */
-       if (dsize < 4 || dsize > sizeof(struct i2c_hid_desc)) {
-               dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
-                       dsize);
+               dev_err(&client->dev, "hid_descr_cmd failed\n");
                return -ENODEV;
        }
 
+       /* Validate the length of HID descriptor, the 4 first bytes:
+        * bytes 0-1 -> length
+        * bytes 2-3 -> bcdVersion (has to be 1.00) */
        /* check bcdVersion == 1.0 */
        if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
                dev_err(&client->dev,
@@ -843,17 +827,14 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
                return -ENODEV;
        }
 
-       i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
-
-       ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer,
-                               dsize);
-       if (ret) {
-               dev_err(&client->dev, "hid_descr_cmd Fail\n");
+       /* Descriptor length should be 30 bytes as per the specification */
+       dsize = le16_to_cpu(hdesc->wHIDDescLength);
+       if (dsize != sizeof(struct i2c_hid_desc)) {
+               dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
+                       dsize);
                return -ENODEV;
        }
-
        i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer);
-
        return 0;
 }
 
index 0d078c3..0cb92e3 100644 (file)
@@ -441,12 +441,11 @@ static int uhid_dev_create2(struct uhid_device *uhid,
        if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)
                return -EINVAL;
 
-       uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL);
+       uhid->rd_data = kmemdup(ev->u.create2.rd_data, uhid->rd_size,
+                               GFP_KERNEL);
        if (!uhid->rd_data)
                return -ENOMEM;
 
-       memcpy(uhid->rd_data, ev->u.create2.rd_data, uhid->rd_size);
-
        hid = hid_allocate_device();
        if (IS_ERR(hid)) {
                ret = PTR_ERR(hid);
index 8e4ddb3..59badc1 100644 (file)
@@ -115,6 +115,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS1, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS },
index 39e7177..089841c 100644 (file)
@@ -479,6 +479,8 @@ config LEDS_OT200
          This option enables support for the LEDs on the Bachmann OT200.
          Say Y to enable LEDs on the Bachmann OT200.
 
+comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)"
+
 config LEDS_BLINKM
        tristate "LED support for the BlinkM I2C RGB LED"
        depends on LEDS_CLASS
index 720e3a1..77632cf 100644 (file)
@@ -233,11 +233,6 @@ struct hid_item {
 #define HID_DG_BARRELSWITCH    0x000d0044
 #define HID_DG_ERASER          0x000d0045
 #define HID_DG_TABLETPICK      0x000d0046
-/*
- * as of May 20, 2009 the usages below are not yet in the official USB spec
- * but are being pushed by Microsft as described in their paper "Digitizer
- * Drivers for Windows Touch and Pen-Based Computers"
- */
 #define HID_DG_CONFIDENCE      0x000d0047
 #define HID_DG_WIDTH           0x000d0048
 #define HID_DG_HEIGHT          0x000d0049
@@ -246,6 +241,8 @@ struct hid_item {
 #define HID_DG_DEVICEINDEX     0x000d0053
 #define HID_DG_CONTACTCOUNT    0x000d0054
 #define HID_DG_CONTACTMAX      0x000d0055
+#define HID_DG_BARRELSWITCH2   0x000d005a
+#define HID_DG_TOOLSERIALNUMBER        0x000d005b
 
 /*
  * HID report types --- Ouch! HID spec says 1 2 3!
@@ -299,12 +296,20 @@ struct hid_item {
 
 /*
  * HID device groups
+ *
+ * Note: HID_GROUP_ANY is declared in linux/mod_devicetable.h
+ * and has a value of 0x0000
  */
 #define HID_GROUP_GENERIC                      0x0001
 #define HID_GROUP_MULTITOUCH                   0x0002
 #define HID_GROUP_SENSOR_HUB                   0x0003
 #define HID_GROUP_MULTITOUCH_WIN_8             0x0004
 
+/*
+ * Vendor specific HID device groups
+ */
+#define HID_GROUP_RMI                          0x0100
+
 /*
  * This is the global environment of the parser. This information is
  * persistent for main-items. The global environment can be saved and
@@ -570,6 +575,8 @@ struct hid_descriptor {
        .bus = BUS_USB, .vendor = (ven), .product = (prod)
 #define HID_BLUETOOTH_DEVICE(ven, prod)                                        \
        .bus = BUS_BLUETOOTH, .vendor = (ven), .product = (prod)
+#define HID_I2C_DEVICE(ven, prod)                              \
+       .bus = BUS_I2C, .vendor = (ven), .product = (prod)
 
 #define HID_REPORT_ID(rep) \
        .report_type = (rep)
index f484952..19df18c 100644 (file)
@@ -462,7 +462,10 @@ struct input_keymap_entry {
 #define KEY_VIDEO_NEXT         241     /* drive next video source */
 #define KEY_VIDEO_PREV         242     /* drive previous video source */
 #define KEY_BRIGHTNESS_CYCLE   243     /* brightness up, after max is min */
-#define KEY_BRIGHTNESS_ZERO    244     /* brightness off, use ambient */
+#define KEY_BRIGHTNESS_AUTO    244     /* Set Auto Brightness: manual
+                                         brightness control is off,
+                                         rely on ambient */
+#define KEY_BRIGHTNESS_ZERO    KEY_BRIGHTNESS_AUTO
 #define KEY_DISPLAY_OFF                245     /* display device to off state */
 
 #define KEY_WWAN               246     /* Wireless WAN (LTE, UMTS, GSM, etc.) */
@@ -632,6 +635,7 @@ struct input_keymap_entry {
 #define KEY_ADDRESSBOOK                0x1ad   /* AL Contacts/Address Book */
 #define KEY_MESSENGER          0x1ae   /* AL Instant Messaging */
 #define KEY_DISPLAYTOGGLE      0x1af   /* Turn display (LCD) on and off */
+#define KEY_BRIGHTNESS_TOGGLE  KEY_DISPLAYTOGGLE
 #define KEY_SPELLCHECK         0x1b0   /* AL Spell Check */
 #define KEY_LOGOFF             0x1b1   /* AL Logoff */
 
@@ -723,6 +727,17 @@ struct input_keymap_entry {
 
 #define KEY_ALS_TOGGLE         0x230   /* Ambient light sensor */
 
+#define KEY_BUTTONCONFIG               0x240   /* AL Button Configuration */
+#define KEY_TASKMANAGER                0x241   /* AL Task/Project Manager */
+#define KEY_JOURNAL            0x242   /* AL Log/Journal/Timecard */
+#define KEY_CONTROLPANEL               0x243   /* AL Control Panel */
+#define KEY_APPSELECT          0x244   /* AL Select Task/Application */
+#define KEY_SCREENSAVER                0x245   /* AL Screen Saver */
+#define KEY_VOICECOMMAND               0x246   /* Listening Voice Command */
+
+#define KEY_BRIGHTNESS_MIN             0x250   /* Set Brightness to Minimum */
+#define KEY_BRIGHTNESS_MAX             0x251   /* Set Brightness to Maximum */
+
 #define BTN_TRIGGER_HAPPY              0x2c0
 #define BTN_TRIGGER_HAPPY1             0x2c0
 #define BTN_TRIGGER_HAPPY2             0x2c1