]> Git Repo - linux.git/commitdiff
Merge branch 'for-4.18/multitouch' into for-linus
authorJiri Kosina <[email protected]>
Fri, 8 Jun 2018 08:25:50 +0000 (10:25 +0200)
committerJiri Kosina <[email protected]>
Fri, 8 Jun 2018 08:25:50 +0000 (10:25 +0200)
- improvement of duplicate usage handling in hid-input from Benjamin Tissoires
- Win 8.1 precisioun touchpad spec implementation from Benjamin Tissoires

1  2 
drivers/hid/hid-core.c
drivers/hid/hid-input.c
include/linux/hid.h

diff --combined drivers/hid/hid-core.c
index 720be70ae74a377eb72781f25b3ad7d8a4a17e33,68819106f4fc0410bd1886d15d20aa3217a047a8..355dc7e4956261e3421f27ca4521d7aac1515ad5
@@@ -57,7 -57,9 +57,9 @@@ MODULE_PARM_DESC(ignore_special_drivers
   * Register a new report for a device.
   */
  
- struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id)
+ struct hid_report *hid_register_report(struct hid_device *device,
+                                      unsigned int type, unsigned int id,
+                                      unsigned int application)
  {
        struct hid_report_enum *report_enum = device->report_enum + type;
        struct hid_report *report;
@@@ -78,6 -80,7 +80,7 @@@
        report->type = type;
        report->size = 0;
        report->device = device;
+       report->application = application;
        report_enum->report_id_hash[id] = report;
  
        list_add_tail(&report->list, &report_enum->report_list);
@@@ -221,11 -224,15 +224,15 @@@ static int hid_add_field(struct hid_par
  {
        struct hid_report *report;
        struct hid_field *field;
-       unsigned usages;
-       unsigned offset;
-       unsigned i;
+       unsigned int usages;
+       unsigned int offset;
+       unsigned int i;
+       unsigned int application;
  
-       report = hid_register_report(parser->device, report_type, parser->global.report_id);
+       application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION);
+       report = hid_register_report(parser->device, report_type,
+                                    parser->global.report_id, application);
        if (!report) {
                hid_err(parser->device, "hid_register_report failed\n");
                return -1;
  
        field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL);
        field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL);
-       field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION);
+       field->application = application;
  
        for (i = 0; i < usages; i++) {
                unsigned j = i;
@@@ -1798,7 -1805,7 +1805,7 @@@ EXPORT_SYMBOL_GPL(hid_hw_stop)
   *
   * Tell underlying HW to start delivering events from the device.
   * This function should be called sometime after successful call
 - * to hid_hiw_start().
 + * to hid_hw_start().
   */
  int hid_hw_open(struct hid_device *hdev)
  {
diff --combined drivers/hid/hid-input.c
index 930652c25120ee14e57859af3bbce8d5072dab26,fea6d4898f1575ca69629d5100e3050648e256b6..ab93dd5927c3af344aeec911fe39bd3621bd787a
@@@ -387,8 -387,7 +387,8 @@@ static int hidinput_get_battery_propert
                break;
  
        case POWER_SUPPLY_PROP_CAPACITY:
 -              if (dev->battery_report_type == HID_FEATURE_REPORT) {
 +              if (dev->battery_status != HID_BATTERY_REPORTED &&
 +                  !dev->battery_avoid_query) {
                        value = hidinput_query_battery_capacity(dev);
                        if (value < 0)
                                return value;
                break;
  
        case POWER_SUPPLY_PROP_STATUS:
 -              if (!dev->battery_reported &&
 -                  dev->battery_report_type == HID_FEATURE_REPORT) {
 +              if (dev->battery_status != HID_BATTERY_REPORTED &&
 +                  !dev->battery_avoid_query) {
                        value = hidinput_query_battery_capacity(dev);
                        if (value < 0)
                                return value;
  
                        dev->battery_capacity = value;
 -                      dev->battery_reported = true;
 +                      dev->battery_status = HID_BATTERY_QUERIED;
                }
  
 -              if (!dev->battery_reported)
 +              if (dev->battery_status == HID_BATTERY_UNKNOWN)
                        val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
                else if (dev->battery_capacity == 100)
                        val->intval = POWER_SUPPLY_STATUS_FULL;
@@@ -487,14 -486,6 +487,14 @@@ static int hidinput_setup_battery(struc
        dev->battery_report_type = report_type;
        dev->battery_report_id = field->report->id;
  
 +      /*
 +       * Stylus is normally not connected to the device and thus we
 +       * can't query the device and get meaningful battery strength.
 +       * We have to wait for the device to report it on its own.
 +       */
 +      dev->battery_avoid_query = report_type == HID_INPUT_REPORT &&
 +                                 field->physical == HID_DG_STYLUS;
 +
        dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
        if (IS_ERR(dev->battery)) {
                error = PTR_ERR(dev->battery);
@@@ -539,10 -530,9 +539,10 @@@ static void hidinput_update_battery(str
  
        capacity = hidinput_scale_battery_capacity(dev, value);
  
 -      if (!dev->battery_reported || capacity != dev->battery_capacity) {
 +      if (dev->battery_status != HID_BATTERY_REPORTED ||
 +          capacity != dev->battery_capacity) {
                dev->battery_capacity = capacity;
 -              dev->battery_reported = true;
 +              dev->battery_status = HID_BATTERY_REPORTED;
                power_supply_changed(dev->battery);
        }
  }
@@@ -1110,8 -1100,31 +1110,31 @@@ mapped
  
        set_bit(usage->type, input->evbit);
  
-       while (usage->code <= max && test_and_set_bit(usage->code, bit))
-               usage->code = find_next_zero_bit(bit, max + 1, usage->code);
+       /*
+        * This part is *really* controversial:
+        * - HID aims at being generic so we should do our best to export
+        *   all incoming events
+        * - HID describes what events are, so there is no reason for ABS_X
+        *   to be mapped to ABS_Y
+        * - HID is using *_MISC+N as a default value, but nothing prevents
+        *   *_MISC+N to overwrite a legitimate even, which confuses userspace
+        *   (for instance ABS_MISC + 7 is ABS_MT_SLOT, which has a different
+        *   processing)
+        *
+        * If devices still want to use this (at their own risk), they will
+        * have to use the quirk HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE, but
+        * the default should be a reliable mapping.
+        */
+       while (usage->code <= max && test_and_set_bit(usage->code, bit)) {
+               if (device->quirks & HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE) {
+                       usage->code = find_next_zero_bit(bit,
+                                                        max + 1,
+                                                        usage->code);
+               } else {
+                       device->status |= HID_STAT_DUP_DETECTED;
+                       goto ignore;
+               }
+       }
  
        if (usage->code > max)
                goto ignore;
@@@ -1487,15 -1500,56 +1510,56 @@@ static void report_features(struct hid_
                }
  }
  
- static struct hid_input *hidinput_allocate(struct hid_device *hid)
+ static struct hid_input *hidinput_allocate(struct hid_device *hid,
+                                          unsigned int application)
  {
        struct hid_input *hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL);
        struct input_dev *input_dev = input_allocate_device();
-       if (!hidinput || !input_dev) {
-               kfree(hidinput);
-               input_free_device(input_dev);
-               hid_err(hid, "Out of memory during hid input probe\n");
-               return NULL;
+       const char *suffix = NULL;
+       if (!hidinput || !input_dev)
+               goto fail;
+       if ((hid->quirks & HID_QUIRK_INPUT_PER_APP) &&
+           hid->maxapplication > 1) {
+               switch (application) {
+               case HID_GD_KEYBOARD:
+                       suffix = "Keyboard";
+                       break;
+               case HID_GD_KEYPAD:
+                       suffix = "Keypad";
+                       break;
+               case HID_GD_MOUSE:
+                       suffix = "Mouse";
+                       break;
+               case HID_DG_STYLUS:
+                       suffix = "Pen";
+                       break;
+               case HID_DG_TOUCHSCREEN:
+                       suffix = "Touchscreen";
+                       break;
+               case HID_DG_TOUCHPAD:
+                       suffix = "Touchpad";
+                       break;
+               case HID_GD_SYSTEM_CONTROL:
+                       suffix = "System Control";
+                       break;
+               case HID_CP_CONSUMER_CONTROL:
+                       suffix = "Consumer Control";
+                       break;
+               case HID_GD_WIRELESS_RADIO_CTLS:
+                       suffix = "Wireless Radio Control";
+                       break;
+               default:
+                       break;
+               }
+       }
+       if (suffix) {
+               hidinput->name = kasprintf(GFP_KERNEL, "%s %s",
+                                          hid->name, suffix);
+               if (!hidinput->name)
+                       goto fail;
        }
  
        input_set_drvdata(input_dev, hid);
        input_dev->setkeycode = hidinput_setkeycode;
        input_dev->getkeycode = hidinput_getkeycode;
  
-       input_dev->name = hid->name;
+       input_dev->name = hidinput->name ? hidinput->name : hid->name;
        input_dev->phys = hid->phys;
        input_dev->uniq = hid->uniq;
        input_dev->id.bustype = hid->bus;
        input_dev->id.product = hid->product;
        input_dev->id.version = hid->version;
        input_dev->dev.parent = &hid->dev;
        hidinput->input = input_dev;
        list_add_tail(&hidinput->list, &hid->inputs);
  
+       INIT_LIST_HEAD(&hidinput->reports);
        return hidinput;
+ fail:
+       kfree(hidinput);
+       input_free_device(input_dev);
+       hid_err(hid, "Out of memory during hid input probe\n");
+       return NULL;
  }
  
  static bool hidinput_has_been_populated(struct hid_input *hidinput)
@@@ -1562,6 -1625,7 +1635,7 @@@ static void hidinput_cleanup_hidinput(s
  
        list_del(&hidinput->list);
        input_free_device(hidinput->input);
+       kfree(hidinput->name);
  
        for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
                if (k == HID_OUTPUT_REPORT &&
@@@ -1594,6 -1658,20 +1668,20 @@@ static struct hid_input *hidinput_match
        return NULL;
  }
  
+ static struct hid_input *hidinput_match_application(struct hid_report *report)
+ {
+       struct hid_device *hid = report->device;
+       struct hid_input *hidinput;
+       list_for_each_entry(hidinput, &hid->inputs, list) {
+               if (hidinput->report &&
+                   hidinput->report->application == report->application)
+                       return hidinput;
+       }
+       return NULL;
+ }
  static inline void hidinput_configure_usages(struct hid_input *hidinput,
                                             struct hid_report *report)
  {
@@@ -1616,11 -1694,14 +1704,14 @@@ int hidinput_connect(struct hid_device 
        struct hid_driver *drv = hid->driver;
        struct hid_report *report;
        struct hid_input *next, *hidinput = NULL;
+       unsigned int application;
        int i, k;
  
        INIT_LIST_HEAD(&hid->inputs);
        INIT_WORK(&hid->led_work, hidinput_led_worker);
  
+       hid->status &= ~HID_STAT_DUP_DETECTED;
        if (!force) {
                for (i = 0; i < hid->maxcollection; i++) {
                        struct hid_collection *col = &hid->collection[i];
                        if (!report->maxfield)
                                continue;
  
+                       application = report->application;
                        /*
                         * Find the previous hidinput report attached
                         * to this report id.
                         */
                        if (hid->quirks & HID_QUIRK_MULTI_INPUT)
                                hidinput = hidinput_match(report);
+                       else if (hid->maxapplication > 1 &&
+                                (hid->quirks & HID_QUIRK_INPUT_PER_APP))
+                               hidinput = hidinput_match_application(report);
  
                        if (!hidinput) {
-                               hidinput = hidinput_allocate(hid);
+                               hidinput = hidinput_allocate(hid, application);
                                if (!hidinput)
                                        goto out_unwind;
                        }
  
                        if (hid->quirks & HID_QUIRK_MULTI_INPUT)
                                hidinput->report = report;
+                       list_add_tail(&report->hidinput_list,
+                                     &hidinput->reports);
                }
        }
  
                goto out_unwind;
        }
  
+       if (hid->status & HID_STAT_DUP_DETECTED)
+               hid_dbg(hid,
+                       "Some usages could not be mapped, please use HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE if this is legitimate.\n");
        return 0;
  
  out_unwind:
diff --combined include/linux/hid.h
index 08d92bb005fd8dd8e7bd6dbd6798dee378953d00,a1be991e1eae40388ce1cab2924ba997a6c77a60..41a3d5775394fed48e7b880317eaf6c1944c2817
@@@ -292,9 -292,12 +292,12 @@@ struct hid_item 
  #define HID_DG_CONTACTCOUNT   0x000d0054
  #define HID_DG_CONTACTMAX     0x000d0055
  #define HID_DG_SCANTIME               0x000d0056
+ #define HID_DG_SURFACESWITCH  0x000d0057
+ #define HID_DG_BUTTONSWITCH   0x000d0058
  #define HID_DG_BUTTONTYPE     0x000d0059
  #define HID_DG_BARRELSWITCH2  0x000d005a
  #define HID_DG_TOOLSERIALNUMBER       0x000d005b
+ #define HID_DG_LATENCYMODE    0x000d0060
  
  #define HID_VD_ASUS_CUSTOM_MEDIA_KEYS 0xff310076
  /*
  /* BIT(8) reserved for backward compatibility, was HID_QUIRK_NO_EMPTY_INPUT */
  /* BIT(9) reserved for backward compatibility, was NO_INIT_INPUT_REPORTS */
  #define HID_QUIRK_ALWAYS_POLL                 BIT(10)
+ #define HID_QUIRK_INPUT_PER_APP                       BIT(11)
  #define HID_QUIRK_SKIP_OUTPUT_REPORTS         BIT(16)
  #define HID_QUIRK_SKIP_OUTPUT_REPORT_ID               BIT(17)
  #define HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP        BIT(18)
  #define HID_QUIRK_HAVE_SPECIAL_DRIVER         BIT(19)
+ #define HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE        BIT(20)
  #define HID_QUIRK_FULLSPEED_INTERVAL          BIT(28)
  #define HID_QUIRK_NO_INIT_REPORTS             BIT(29)
  #define HID_QUIRK_NO_IGNORE                   BIT(30)
  #define HID_GROUP_RMI                         0x0100
  #define HID_GROUP_WACOM                               0x0101
  #define HID_GROUP_LOGITECH_DJ_DEVICE          0x0102
 +#define HID_GROUP_STEAM                               0x0103
  
  /*
   * HID protocol status
@@@ -464,8 -468,10 +469,10 @@@ struct hid_field 
  
  struct hid_report {
        struct list_head list;
-       unsigned id;                                    /* id of this report */
-       unsigned type;                                  /* report type */
+       struct list_head hidinput_list;
+       unsigned int id;                                /* id of this report */
+       unsigned int type;                              /* report type */
+       unsigned int application;                       /* application usage for this report */
        struct hid_field *field[HID_MAX_FIELDS];        /* fields of the report */
        unsigned maxfield;                              /* maximum valid field index */
        unsigned size;                                  /* size of the report (bits) */
@@@ -503,12 -509,15 +510,15 @@@ struct hid_output_fifo 
  
  #define HID_STAT_ADDED                BIT(0)
  #define HID_STAT_PARSED               BIT(1)
+ #define HID_STAT_DUP_DETECTED BIT(2)
  
  struct hid_input {
        struct list_head list;
        struct hid_report *report;
        struct input_dev *input;
+       const char *name;
        bool registered;
+       struct list_head reports;       /* the list of reports */
  };
  
  enum hid_type {
        HID_TYPE_USBNONE
  };
  
 +enum hid_battery_status {
 +      HID_BATTERY_UNKNOWN = 0,
 +      HID_BATTERY_QUERIED,            /* Kernel explicitly queried battery strength */
 +      HID_BATTERY_REPORTED,           /* Device sent unsolicited battery strength report */
 +};
 +
  struct hid_driver;
  struct hid_ll_driver;
  
@@@ -565,8 -568,7 +575,8 @@@ struct hid_device {                                                        /* device rep
        __s32 battery_max;
        __s32 battery_report_type;
        __s32 battery_report_id;
 -      bool battery_reported;
 +      enum hid_battery_status battery_status;
 +      bool battery_avoid_query;
  #endif
  
        unsigned int status;                                            /* see STAT flags above */
@@@ -865,7 -867,9 +875,9 @@@ void hid_output_report(struct hid_repor
  void __hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype);
  u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags);
  struct hid_device *hid_allocate_device(void);
- struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);
+ struct hid_report *hid_register_report(struct hid_device *device,
+                                      unsigned int type, unsigned int id,
+                                      unsigned int application);
  int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
  struct hid_report *hid_validate_values(struct hid_device *hid,
                                       unsigned int type, unsigned int id,
This page took 0.095833 seconds and 4 git commands to generate.