]> nv-tegra.nvidia Code Review - linux-3.10.git/blobdiff - drivers/hid/hid-input.c
Merge linux-3.10.67 into dev-kernel-3.10
[linux-3.10.git] / drivers / hid / hid-input.c
index d8cc790238489119445db3f6b4a0f1234f33e889..15ca1177dd5449568658c4ea2b37441d1acbd539 100644 (file)
@@ -1,6 +1,7 @@
 /*
  *  Copyright (c) 2000-2001 Vojtech Pavlik
  *  Copyright (c) 2006-2010 Jiri Kosina
+ *  Copyright (c) 2014 NVIDIA Corporation. All rights reserved.
  *
  *  HID to Linux Input mapping
  */
 #include <linux/hid.h>
 #include <linux/hid-debug.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/hid-input.h>
+
+#include "hid-ids.h"
+
 #define unk    KEY_UNKNOWN
 
 static const unsigned char hid_keyboard[256] = {
@@ -44,11 +50,11 @@ static const unsigned char hid_keyboard[256] = {
         72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
        191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
        115,114,unk,unk,unk,121,unk, 89, 93,124, 92, 94, 95,unk,unk,unk,
-       122,123, 90, 91, 85,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+       122,123, 90, 91, 85,unk,unk,unk,unk,unk,unk,unk,111,unk,unk,unk,
        unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
        unk,unk,unk,unk,unk,unk,179,180,unk,unk,unk,unk,unk,unk,unk,unk,
        unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
-       unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+       unk,unk,unk,unk,unk,unk,unk,unk,111,unk,unk,unk,unk,unk,unk,unk,
         29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
        150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk
 };
@@ -68,39 +74,52 @@ static const struct {
 #define map_key_clear(c)       hid_map_usage_clear(hidinput, usage, &bit, \
                &max, EV_KEY, (c))
 
-static inline int match_scancode(unsigned int code, unsigned int scancode)
+static bool match_scancode(struct hid_usage *usage,
+                          unsigned int cur_idx, unsigned int scancode)
 {
-       if (scancode == 0)
-               return 1;
-
-       return (code & (HID_USAGE_PAGE | HID_USAGE)) == scancode;
+       return (usage->hid & (HID_USAGE_PAGE | HID_USAGE)) == scancode;
 }
 
-static inline int match_keycode(unsigned int code, unsigned int keycode)
+static bool match_keycode(struct hid_usage *usage,
+                         unsigned int cur_idx, unsigned int keycode)
 {
-       if (keycode == 0)
-               return 1;
+       /*
+        * We should exclude unmapped usages when doing lookup by keycode.
+        */
+       return (usage->type == EV_KEY && usage->code == keycode);
+}
 
-       return code == keycode;
+static bool match_index(struct hid_usage *usage,
+                       unsigned int cur_idx, unsigned int idx)
+{
+       return cur_idx == idx;
 }
 
+typedef bool (*hid_usage_cmp_t)(struct hid_usage *usage,
+                               unsigned int cur_idx, unsigned int val);
+
 static struct hid_usage *hidinput_find_key(struct hid_device *hid,
-                                          unsigned int scancode,
-                                          unsigned int keycode)
+                                          hid_usage_cmp_t match,
+                                          unsigned int value,
+                                          unsigned int *usage_idx)
 {
-       int i, j, k;
+       unsigned int i, j, k, cur_idx = 0;
        struct hid_report *report;
        struct hid_usage *usage;
 
        for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
                list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
                        for (i = 0; i < report->maxfield; i++) {
-                               for ( j = 0; j < report->field[i]->maxusage; j++) {
+                               for (j = 0; j < report->field[i]->maxusage; j++) {
                                        usage = report->field[i]->usage + j;
-                                       if (usage->type == EV_KEY &&
-                                               match_scancode(usage->hid, scancode) &&
-                                               match_keycode(usage->code, keycode))
-                                               return usage;
+                                       if (usage->type == EV_KEY || usage->type == 0) {
+                                               if (match(usage, cur_idx, value)) {
+                                                       if (usage_idx)
+                                                               *usage_idx = cur_idx;
+                                                       return usage;
+                                               }
+                                               cur_idx++;
+                                       }
                                }
                        }
                }
@@ -108,39 +127,68 @@ static struct hid_usage *hidinput_find_key(struct hid_device *hid,
        return NULL;
 }
 
+static struct hid_usage *hidinput_locate_usage(struct hid_device *hid,
+                                       const struct input_keymap_entry *ke,
+                                       unsigned int *index)
+{
+       struct hid_usage *usage;
+       unsigned int scancode;
+
+       if (ke->flags & INPUT_KEYMAP_BY_INDEX)
+               usage = hidinput_find_key(hid, match_index, ke->index, index);
+       else if (input_scancode_to_scalar(ke, &scancode) == 0)
+               usage = hidinput_find_key(hid, match_scancode, scancode, index);
+       else
+               usage = NULL;
+
+       return usage;
+}
+
 static int hidinput_getkeycode(struct input_dev *dev,
-                              unsigned int scancode, unsigned int *keycode)
+                              struct input_keymap_entry *ke)
 {
        struct hid_device *hid = input_get_drvdata(dev);
        struct hid_usage *usage;
+       unsigned int scancode, index;
 
-       usage = hidinput_find_key(hid, scancode, 0);
+       usage = hidinput_locate_usage(hid, ke, &index);
        if (usage) {
-               *keycode = usage->code;
+               ke->keycode = usage->type == EV_KEY ?
+                               usage->code : KEY_RESERVED;
+               ke->index = index;
+               scancode = usage->hid & (HID_USAGE_PAGE | HID_USAGE);
+               ke->len = sizeof(scancode);
+               memcpy(ke->scancode, &scancode, sizeof(scancode));
                return 0;
        }
+
        return -EINVAL;
 }
 
 static int hidinput_setkeycode(struct input_dev *dev,
-                              unsigned int scancode, unsigned int keycode)
+                              const struct input_keymap_entry *ke,
+                              unsigned int *old_keycode)
 {
        struct hid_device *hid = input_get_drvdata(dev);
        struct hid_usage *usage;
-       int old_keycode;
 
-       usage = hidinput_find_key(hid, scancode, 0);
+       usage = hidinput_locate_usage(hid, ke, NULL);
        if (usage) {
-               old_keycode = usage->code;
-               usage->code = keycode;
+               *old_keycode = usage->type == EV_KEY ?
+                               usage->code : KEY_RESERVED;
+               usage->code = ke->keycode;
 
-               clear_bit(old_keycode, dev->keybit);
+               clear_bit(*old_keycode, dev->keybit);
                set_bit(usage->code, dev->keybit);
-               dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode);
-               /* Set the keybit for the old keycode if the old keycode is used
-                * by another key */
-               if (hidinput_find_key (hid, 0, old_keycode))
-                       set_bit(old_keycode, dev->keybit);
+               dbg_hid("Assigned keycode %d to HID usage code %x\n",
+                       usage->code, usage->hid);
+
+               /*
+                * Set the keybit for the old keycode if the old keycode is used
+                * by another key
+                */
+               if (hidinput_find_key(hid, match_keycode, *old_keycode, NULL))
+                       set_bit(*old_keycode, dev->keybit);
 
                return 0;
        }
@@ -148,6 +196,288 @@ static int hidinput_setkeycode(struct input_dev *dev,
        return -EINVAL;
 }
 
+/**
+ * hidinput_calc_abs_res - calculate an absolute axis resolution
+ * @field: the HID report field to calculate resolution for
+ * @code: axis code
+ *
+ * The formula is:
+ *                         (logical_maximum - logical_minimum)
+ * resolution = ----------------------------------------------------------
+ *              (physical_maximum - physical_minimum) * 10 ^ unit_exponent
+ *
+ * as seen in the HID specification v1.11 6.2.2.7 Global Items.
+ *
+ * Only exponent 1 length units are processed. Centimeters and inches are
+ * converted to millimeters. Degrees are converted to radians.
+ */
+__s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
+{
+       __s32 unit_exponent = field->unit_exponent;
+       __s32 logical_extents = field->logical_maximum -
+                                       field->logical_minimum;
+       __s32 physical_extents = field->physical_maximum -
+                                       field->physical_minimum;
+       __s32 prev;
+
+       /* Check if the extents are sane */
+       if (logical_extents <= 0 || physical_extents <= 0)
+               return 0;
+
+       /*
+        * Verify and convert units.
+        * See HID specification v1.11 6.2.2.7 Global Items for unit decoding
+        */
+       switch (code) {
+       case ABS_X:
+       case ABS_Y:
+       case ABS_Z:
+       case ABS_MT_POSITION_X:
+       case ABS_MT_POSITION_Y:
+       case ABS_MT_TOOL_X:
+       case ABS_MT_TOOL_Y:
+       case ABS_MT_TOUCH_MAJOR:
+       case ABS_MT_TOUCH_MINOR:
+               if (field->unit & 0xffffff00)           /* Not a length */
+                       return 0;
+               unit_exponent += hid_snto32(field->unit >> 4, 4) - 1;
+               switch (field->unit & 0xf) {
+               case 0x1:                               /* If centimeters */
+                       /* Convert to millimeters */
+                       unit_exponent += 1;
+                       break;
+               case 0x3:                               /* If inches */
+                       /* Convert to millimeters */
+                       prev = physical_extents;
+                       physical_extents *= 254;
+                       if (physical_extents < prev)
+                               return 0;
+                       unit_exponent -= 1;
+                       break;
+               default:
+                       return 0;
+               }
+               break;
+
+       case ABS_RX:
+       case ABS_RY:
+       case ABS_RZ:
+       case ABS_TILT_X:
+       case ABS_TILT_Y:
+               if (field->unit == 0x14) {              /* If degrees */
+                       /* Convert to radians */
+                       prev = logical_extents;
+                       logical_extents *= 573;
+                       if (logical_extents < prev)
+                               return 0;
+                       unit_exponent += 1;
+               } else if (field->unit != 0x12) {       /* If not radians */
+                       return 0;
+               }
+               break;
+
+       default:
+               return 0;
+       }
+
+       /* Apply negative unit exponent */
+       for (; unit_exponent < 0; unit_exponent++) {
+               prev = logical_extents;
+               logical_extents *= 10;
+               if (logical_extents < prev)
+                       return 0;
+       }
+       /* Apply positive unit exponent */
+       for (; unit_exponent > 0; unit_exponent--) {
+               prev = physical_extents;
+               physical_extents *= 10;
+               if (physical_extents < prev)
+                       return 0;
+       }
+
+       /* Calculate resolution */
+       return DIV_ROUND_CLOSEST(logical_extents, physical_extents);
+}
+EXPORT_SYMBOL_GPL(hidinput_calc_abs_res);
+
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+static enum power_supply_property hidinput_battery_props[] = {
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_SCOPE,
+};
+
+#define HID_BATTERY_QUIRK_PERCENT      (1 << 0) /* always reports percent */
+#define HID_BATTERY_QUIRK_FEATURE      (1 << 1) /* ask for feature report */
+
+static const struct hid_device_id hid_battery_quirks[] = {
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+                       USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO),
+       HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+                              USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI),
+         HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+                              USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO),
+         HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+               USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI),
+         HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE },
+       {}
+};
+
+static unsigned find_battery_quirk(struct hid_device *hdev)
+{
+       unsigned quirks = 0;
+       const struct hid_device_id *match;
+
+       match = hid_match_id(hdev, hid_battery_quirks);
+       if (match != NULL)
+               quirks = match->driver_data;
+
+       return quirks;
+}
+
+static int hidinput_get_battery_property(struct power_supply *psy,
+                                        enum power_supply_property prop,
+                                        union power_supply_propval *val)
+{
+       struct hid_device *dev = container_of(psy, struct hid_device, battery);
+       int ret = 0;
+       __u8 *buf;
+
+       switch (prop) {
+       case POWER_SUPPLY_PROP_PRESENT:
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = 1;
+               break;
+
+       case POWER_SUPPLY_PROP_CAPACITY:
+
+               buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL);
+               if (!buf) {
+                       ret = -ENOMEM;
+                       break;
+               }
+               ret = dev->hid_get_raw_report(dev, dev->battery_report_id,
+                                             buf, 2,
+                                             dev->battery_report_type);
+
+               if (ret != 2) {
+                       if (ret >= 0)
+                               ret = -EINVAL;
+                       kfree(buf);
+                       break;
+               }
+
+               if (dev->battery_min < dev->battery_max &&
+                   buf[1] >= dev->battery_min &&
+                   buf[1] <= dev->battery_max)
+                       val->intval = (100 * (buf[1] - dev->battery_min)) /
+                               (dev->battery_max - dev->battery_min);
+               kfree(buf);
+               break;
+
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = dev->name;
+               break;
+
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               break;
+
+       case POWER_SUPPLY_PROP_SCOPE:
+               val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field)
+{
+       struct power_supply *battery = &dev->battery;
+       int ret;
+       unsigned quirks;
+       s32 min, max;
+
+       if (field->usage->hid != HID_DC_BATTERYSTRENGTH)
+               return false;   /* no match */
+
+       if (battery->name != NULL)
+               goto out;       /* already initialized? */
+
+       battery->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq);
+       if (battery->name == NULL)
+               goto out;
+
+       battery->type = POWER_SUPPLY_TYPE_BATTERY;
+       battery->properties = hidinput_battery_props;
+       battery->num_properties = ARRAY_SIZE(hidinput_battery_props);
+       battery->use_for_apm = 0;
+       battery->get_property = hidinput_get_battery_property;
+
+       quirks = find_battery_quirk(dev);
+
+       hid_dbg(dev, "device %x:%x:%x %d quirks %d\n",
+               dev->bus, dev->vendor, dev->product, dev->version, quirks);
+
+       min = field->logical_minimum;
+       max = field->logical_maximum;
+
+       if (quirks & HID_BATTERY_QUIRK_PERCENT) {
+               min = 0;
+               max = 100;
+       }
+
+       if (quirks & HID_BATTERY_QUIRK_FEATURE)
+               report_type = HID_FEATURE_REPORT;
+
+       dev->battery_min = min;
+       dev->battery_max = max;
+       dev->battery_report_type = report_type;
+       dev->battery_report_id = field->report->id;
+
+       ret = power_supply_register(&dev->dev, battery);
+       if (ret != 0) {
+               hid_warn(dev, "can't register power supply: %d\n", ret);
+               kfree(battery->name);
+               battery->name = NULL;
+       }
+
+       power_supply_powers(battery, &dev->dev);
+
+out:
+       return true;
+}
+
+static void hidinput_cleanup_battery(struct hid_device *dev)
+{
+       if (!dev->battery.name)
+               return;
+
+       power_supply_unregister(&dev->battery);
+       kfree(dev->battery.name);
+       dev->battery.name = NULL;
+}
+#else  /* !CONFIG_HID_BATTERY_STRENGTH */
+static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
+                                  struct hid_field *field)
+{
+       return false;
+}
+
+static void hidinput_cleanup_battery(struct hid_device *dev)
+{
+}
+#endif /* CONFIG_HID_BATTERY_STRENGTH */
 
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
                                     struct hid_usage *usage)
@@ -162,6 +492,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
        if (field->flags & HID_MAIN_ITEM_CONSTANT)
                goto ignore;
 
+       /* Ignore if report count is out of bounds. */
+       if (field->report_count < 1)
+               goto ignore;
+
        /* only LED usages are supported in output fields */
        if (field->report_type == HID_OUTPUT_REPORT &&
                        (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
@@ -197,21 +531,26 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
 
                switch (field->application) {
                case HID_GD_MOUSE:
-               case HID_GD_POINTER:  code += 0x110; break;
+               case HID_GD_POINTER:  code += BTN_MOUSE; break;
                case HID_GD_JOYSTICK:
                                if (code <= 0xf)
                                        code += BTN_JOYSTICK;
                                else
-                                       code += BTN_TRIGGER_HAPPY;
+                                       code += BTN_TRIGGER_HAPPY - 0x10;
+                               break;
+               case HID_GD_GAMEPAD:
+                               if (code <= 0xf)
+                                       code += BTN_GAMEPAD;
+                               else
+                                       code += BTN_TRIGGER_HAPPY - 0x10;
                                break;
-               case HID_GD_GAMEPAD:  code += 0x130; break;
                default:
                        switch (field->physical) {
                        case HID_GD_MOUSE:
-                       case HID_GD_POINTER:  code += 0x110; break;
-                       case HID_GD_JOYSTICK: code += 0x120; break;
-                       case HID_GD_GAMEPAD:  code += 0x130; break;
-                       default:              code += 0x100;
+                       case HID_GD_POINTER:  code += BTN_MOUSE; break;
+                       case HID_GD_JOYSTICK: code += BTN_JOYSTICK; break;
+                       case HID_GD_GAMEPAD:  code += BTN_GAMEPAD; break;
+                       default:              code += BTN_MISC;
                        }
                }
 
@@ -235,6 +574,18 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                        case 0x1: map_key_clear(KEY_POWER);  break;
                        case 0x2: map_key_clear(KEY_SLEEP);  break;
                        case 0x3: map_key_clear(KEY_WAKEUP); break;
+                       case 0x4: map_key_clear(KEY_CONTEXT_MENU); break;
+                       case 0x5: map_key_clear(KEY_MENU); break;
+                       case 0x6: map_key_clear(KEY_PROG1); break;
+                       case 0x7: map_key_clear(KEY_HELP); break;
+                       case 0x8: map_key_clear(KEY_EXIT); break;
+                       case 0x9: map_key_clear(KEY_SELECT); break;
+                       case 0xa: map_key_clear(KEY_RIGHT); break;
+                       case 0xb: map_key_clear(KEY_LEFT); break;
+                       case 0xc: map_key_clear(KEY_UP); break;
+                       case 0xd: map_key_clear(KEY_DOWN); break;
+                       case 0xe: map_key_clear(KEY_POWER2); break;
+                       case 0xf: map_key_clear(KEY_RESTART); break;
                        default: goto unknown;
                        }
                        break;
@@ -325,6 +676,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                        map_key_clear(BTN_TOOL_RUBBER);
                        break;
 
+               case 0x3d: /* X Tilt */
+                       map_abs_clear(ABS_TILT_X);
+                       break;
+
+               case 0x3e: /* Y Tilt */
+                       map_abs_clear(ABS_TILT_Y);
+                       break;
+
                case 0x33: /* Touch */
                case 0x42: /* TipSwitch */
                case 0x43: /* TipSwitch2 */
@@ -336,20 +695,54 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                        map_key_clear(BTN_STYLUS);
                        break;
 
+               case 0x46: /* TabletPick */
+                       map_key_clear(BTN_STYLUS2);
+                       break;
+
                default:  goto unknown;
                }
                break;
 
-       case HID_UP_CONSUMER:   /* USB HUT v1.1, pages 56-62 */
+       case HID_UP_CONSUMER:   /* USB HUT v1.12, pages 75-84 */
                switch (usage->hid & HID_USAGE) {
                case 0x000: goto ignore;
+               case 0x030: map_key_clear(KEY_POWER);           break;
+               case 0x031: map_key_clear(KEY_RESTART);         break;
+               case 0x032: map_key_clear(KEY_SLEEP);           break;
                case 0x034: map_key_clear(KEY_SLEEP);           break;
+               case 0x035: map_key_clear(KEY_KBDILLUMTOGGLE);  break;
                case 0x036: map_key_clear(BTN_MISC);            break;
 
-               case 0x040: map_key_clear(KEY_MENU);            break;
-               case 0x045: map_key_clear(KEY_RADIO);           break;
-
+               case 0x040: map_key_clear(KEY_MENU);            break; /* Menu */
+               case 0x041: map_key_clear(KEY_SELECT);          break; /* Menu Pick */
+               case 0x042: map_key_clear(KEY_UP);              break; /* Menu Up */
+               case 0x043: map_key_clear(KEY_DOWN);            break; /* Menu Down */
+               case 0x044: map_key_clear(KEY_LEFT);            break; /* Menu Left */
+               case 0x045: map_key_clear(KEY_RIGHT);           break; /* Menu Right */
+               case 0x046: map_key_clear(KEY_ESC);             break; /* Menu Escape */
+               case 0x047: map_key_clear(KEY_KPPLUS);          break; /* Menu Value Increase */
+               case 0x048: map_key_clear(KEY_KPMINUS);         break; /* Menu Value Decrease */
+
+               case 0x060: map_key_clear(KEY_INFO);            break; /* Data On Screen */
+               case 0x061: map_key_clear(KEY_SUBTITLE);        break; /* Closed Caption */
+               case 0x063: map_key_clear(KEY_VCR);             break; /* VCR/TV */
+               case 0x065: map_key_clear(KEY_CAMERA);          break; /* Snapshot */
+               case 0x069: map_key_clear(KEY_RED);             break;
+               case 0x06a: map_key_clear(KEY_GREEN);           break;
+               case 0x06b: map_key_clear(KEY_BLUE);            break;
+               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;
                case 0x088: map_key_clear(KEY_PC);              break;
                case 0x089: map_key_clear(KEY_TV);              break;
                case 0x08a: map_key_clear(KEY_WWW);             break;
@@ -383,14 +776,19 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x0b7: map_key_clear(KEY_STOPCD);          break;
                case 0x0b8: map_key_clear(KEY_EJECTCD);         break;
                case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT);    break;
+               case 0x0b9: map_key_clear(KEY_SHUFFLE);         break;
+               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;
                case 0x0e9: map_key_clear(KEY_VOLUMEUP);        break;
                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;
@@ -404,18 +802,29 @@ 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;
                case 0x194: map_key_clear(KEY_FILE);            break;
                case 0x196: map_key_clear(KEY_WWW);             break;
                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 0x1b6: map_key_clear(KEY_MEDIA);           break;
-               case 0x1b7: map_key_clear(KEY_SOUND);           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;
+               case 0x1b8: map_key_clear(KEY_VIDEO);           break;
                case 0x1bc: map_key_clear(KEY_MESSENGER);       break;
                case 0x1bd: map_key_clear(KEY_INFO);            break;
                case 0x201: map_key_clear(KEY_NEW);             break;
@@ -444,7 +853,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x233: map_key_clear(KEY_SCROLLUP);        break;
                case 0x234: map_key_clear(KEY_SCROLLDOWN);      break;
                case 0x238: map_rel(REL_HWHEEL);                break;
+               case 0x23d: map_key_clear(KEY_EDIT);            break;
                case 0x25f: map_key_clear(KEY_CANCEL);          break;
+               case 0x269: map_key_clear(KEY_INSERT);          break;
+               case 0x26a: map_key_clear(KEY_DELETE);          break;
                case 0x279: map_key_clear(KEY_REDO);            break;
 
                case 0x289: map_key_clear(KEY_REPLY);           break;
@@ -455,6 +867,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                }
                break;
 
+       case HID_UP_GENDEVCTRLS:
+               if (hidinput_setup_battery(device, HID_INPUT_REPORT, field))
+                       goto ignore;
+               else
+                       goto unknown;
+               break;
+
        case HID_UP_HPVENDOR:   /* Reported on a Dutch layout HP5308 */
                set_bit(EV_REP, input->evbit);
                switch (usage->hid & HID_USAGE) {
@@ -474,6 +893,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                }
                break;
 
+       case HID_UP_HPVENDOR2:
+               set_bit(EV_REP, input->evbit);
+               switch (usage->hid & HID_USAGE) {
+               case 0x003: map_key_clear(KEY_BRIGHTNESSDOWN);  break;
+               case 0x004: map_key_clear(KEY_BRIGHTNESSUP);    break;
+               default:    goto ignore;
+               }
+               break;
+
        case HID_UP_MSVENDOR:
                goto ignore;
 
@@ -537,6 +965,12 @@ mapped:
                        input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4);
                else    input_set_abs_params(input, usage->code, a, b, 0, 0);
 
+               input_abs_set_res(input, usage->code,
+                                 hidinput_calc_abs_res(field, usage->code));
+
+               /* use a larger default input buffer for MT devices */
+               if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0)
+                       input_set_events_per_packet(input, 60);
        }
 
        if (usage->type == EV_ABS &&
@@ -583,6 +1017,8 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
        if (!usage->type)
                return;
 
+       trace_hidinput_hid_event("hid_event");
+
        if (usage->hat_min < usage->hat_max || usage->hat_dir) {
                int hat_dir = usage->hat_dir;
                if (!hat_dir)
@@ -642,6 +1078,17 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
                return;
        }
 
+       /*
+        * Ignore out-of-range values as per HID specification,
+        * section 5.10 and 6.2.25
+        */
+       if ((field->flags & HID_MAIN_ITEM_VARIABLE) &&
+           (value < field->logical_minimum ||
+            value > field->logical_maximum)) {
+               dbg_hid("Ignoring out-of-range value %x\n", value);
+               return;
+       }
+
        /* report the usage code as scancode if the key status has changed */
        if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value)
                input_event(input, EV_MSC, MSC_SCAN, usage->hid);
@@ -656,6 +1103,9 @@ void hidinput_report_event(struct hid_device *hid, struct hid_report *report)
 {
        struct hid_input *hidinput;
 
+       if (hid->quirks & HID_QUIRK_NO_INPUT_SYNC)
+               return;
+
        list_for_each_entry(hidinput, &hid->inputs, list)
                input_sync(hidinput->input);
 }
@@ -678,18 +1128,178 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int
 }
 EXPORT_SYMBOL_GPL(hidinput_find_field);
 
+struct hid_field *hidinput_get_led_field(struct hid_device *hid)
+{
+       struct hid_report *report;
+       struct hid_field *field;
+       int i, j;
+
+       list_for_each_entry(report,
+                           &hid->report_enum[HID_OUTPUT_REPORT].report_list,
+                           list) {
+               for (i = 0; i < report->maxfield; i++) {
+                       field = report->field[i];
+                       for (j = 0; j < field->maxusage; j++)
+                               if (field->usage[j].type == EV_LED)
+                                       return field;
+               }
+       }
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(hidinput_get_led_field);
+
+unsigned int hidinput_count_leds(struct hid_device *hid)
+{
+       struct hid_report *report;
+       struct hid_field *field;
+       int i, j;
+       unsigned int count = 0;
+
+       list_for_each_entry(report,
+                           &hid->report_enum[HID_OUTPUT_REPORT].report_list,
+                           list) {
+               for (i = 0; i < report->maxfield; i++) {
+                       field = report->field[i];
+                       for (j = 0; j < field->maxusage; j++)
+                               if (field->usage[j].type == EV_LED &&
+                                   field->value[j])
+                                       count += 1;
+               }
+       }
+       return count;
+}
+EXPORT_SYMBOL_GPL(hidinput_count_leds);
+
 static int hidinput_open(struct input_dev *dev)
 {
        struct hid_device *hid = input_get_drvdata(dev);
 
-       return hid->ll_driver->open(hid);
+       return hid_hw_open(hid);
 }
 
 static void hidinput_close(struct input_dev *dev)
 {
        struct hid_device *hid = input_get_drvdata(dev);
 
-       hid->ll_driver->close(hid);
+       hid_hw_close(hid);
+}
+
+static void report_features(struct hid_device *hid)
+{
+       struct hid_driver *drv = hid->driver;
+       struct hid_report_enum *rep_enum;
+       struct hid_report *rep;
+       int i, j;
+
+       rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
+       list_for_each_entry(rep, &rep_enum->report_list, list)
+               for (i = 0; i < rep->maxfield; i++) {
+                       /* Ignore if report count is out of bounds. */
+                       if (rep->field[i]->report_count < 1)
+                               continue;
+
+                       for (j = 0; j < rep->field[i]->maxusage; j++) {
+                               /* Verify if Battery Strength feature is available */
+                               hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]);
+
+                               if (drv->feature_mapping)
+                                       drv->feature_mapping(hid, rep->field[i],
+                                                            rep->field[i]->usage + j);
+                       }
+               }
+}
+
+static struct hid_input *hidinput_allocate(struct hid_device *hid)
+{
+       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;
+       }
+
+       input_set_drvdata(input_dev, hid);
+       input_dev->event = hid->ll_driver->hidinput_input_event;
+       input_dev->open = hidinput_open;
+       input_dev->close = hidinput_close;
+       input_dev->setkeycode = hidinput_setkeycode;
+       input_dev->getkeycode = hidinput_getkeycode;
+
+       input_dev->name = hid->name;
+       input_dev->phys = hid->phys;
+       input_dev->uniq = hid->uniq;
+       input_dev->id.bustype = hid->bus;
+       input_dev->id.vendor  = hid->vendor;
+       input_dev->id.product = hid->product;
+       input_dev->id.version = hid->version;
+       input_dev->dev.parent = hid->dev.parent;
+       hidinput->input = input_dev;
+       list_add_tail(&hidinput->list, &hid->inputs);
+
+       return hidinput;
+}
+
+static bool hidinput_has_been_populated(struct hid_input *hidinput)
+{
+       int i;
+       unsigned long r = 0;
+
+       for (i = 0; i < BITS_TO_LONGS(EV_CNT); i++)
+               r |= hidinput->input->evbit[i];
+
+       for (i = 0; i < BITS_TO_LONGS(KEY_CNT); i++)
+               r |= hidinput->input->keybit[i];
+
+       for (i = 0; i < BITS_TO_LONGS(REL_CNT); i++)
+               r |= hidinput->input->relbit[i];
+
+       for (i = 0; i < BITS_TO_LONGS(ABS_CNT); i++)
+               r |= hidinput->input->absbit[i];
+
+       for (i = 0; i < BITS_TO_LONGS(MSC_CNT); i++)
+               r |= hidinput->input->mscbit[i];
+
+       for (i = 0; i < BITS_TO_LONGS(LED_CNT); i++)
+               r |= hidinput->input->ledbit[i];
+
+       for (i = 0; i < BITS_TO_LONGS(SND_CNT); i++)
+               r |= hidinput->input->sndbit[i];
+
+       for (i = 0; i < BITS_TO_LONGS(FF_CNT); i++)
+               r |= hidinput->input->ffbit[i];
+
+       for (i = 0; i < BITS_TO_LONGS(SW_CNT); i++)
+               r |= hidinput->input->swbit[i];
+
+       return !!r;
+}
+
+static void hidinput_cleanup_hidinput(struct hid_device *hid,
+               struct hid_input *hidinput)
+{
+       struct hid_report *report;
+       int i, k;
+
+       list_del(&hidinput->list);
+       input_free_device(hidinput->input);
+
+       for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
+               if (k == HID_OUTPUT_REPORT &&
+                       hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS)
+                       continue;
+
+               list_for_each_entry(report, &hid->report_enum[k].report_list,
+                                   list) {
+
+                       for (i = 0; i < report->maxfield; i++)
+                               if (report->field[i]->hidinput == hidinput)
+                                       report->field[i]->hidinput = NULL;
+               }
+       }
+
+       kfree(hidinput);
 }
 
 /*
@@ -700,11 +1310,10 @@ static void hidinput_close(struct input_dev *dev)
 
 int hidinput_connect(struct hid_device *hid, unsigned int force)
 {
+       struct hid_driver *drv = hid->driver;
        struct hid_report *report;
        struct hid_input *hidinput = NULL;
-       struct input_dev *input_dev;
        int i, j, k;
-       int max_report_type = HID_OUTPUT_REPORT;
 
        INIT_LIST_HEAD(&hid->inputs);
 
@@ -721,43 +1330,22 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
                        return -1;
        }
 
-       if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS)
-               max_report_type = HID_INPUT_REPORT;
+       report_features(hid);
+
+       for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
+               if (k == HID_OUTPUT_REPORT &&
+                       hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS)
+                       continue;
 
-       for (k = HID_INPUT_REPORT; k <= max_report_type; k++)
                list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
 
                        if (!report->maxfield)
                                continue;
 
                        if (!hidinput) {
-                               hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL);
-                               input_dev = input_allocate_device();
-                               if (!hidinput || !input_dev) {
-                                       kfree(hidinput);
-                                       input_free_device(input_dev);
-                                       err_hid("Out of memory during hid input probe");
+                               hidinput = hidinput_allocate(hid);
+                               if (!hidinput)
                                        goto out_unwind;
-                               }
-
-                               input_set_drvdata(input_dev, hid);
-                               input_dev->event =
-                                       hid->ll_driver->hidinput_input_event;
-                               input_dev->open = hidinput_open;
-                               input_dev->close = hidinput_close;
-                               input_dev->setkeycode = hidinput_setkeycode;
-                               input_dev->getkeycode = hidinput_getkeycode;
-
-                               input_dev->name = hid->name;
-                               input_dev->phys = hid->phys;
-                               input_dev->uniq = hid->uniq;
-                               input_dev->id.bustype = hid->bus;
-                               input_dev->id.vendor  = hid->vendor;
-                               input_dev->id.product = hid->product;
-                               input_dev->id.version = hid->version;
-                               input_dev->dev.parent = hid->dev.parent;
-                               hidinput->input = input_dev;
-                               list_add_tail(&hidinput->list, &hid->inputs);
                        }
 
                        for (i = 0; i < report->maxfield; i++)
@@ -765,6 +1353,10 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
                                        hidinput_configure_usage(hidinput, report->field[i],
                                                                 report->field[i]->usage + j);
 
+                       if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
+                           !hidinput_has_been_populated(hidinput))
+                               continue;
+
                        if (hid->quirks & HID_QUIRK_MULTI_INPUT) {
                                /* This will leave hidinput NULL, so that it
                                 * allocates another one if we have more inputs on
@@ -772,18 +1364,40 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
                                 * UGCI) cram a lot of unrelated inputs into the
                                 * same interface. */
                                hidinput->report = report;
+                               if (drv->input_configured &&
+                                   drv->input_configured(hid, hidinput))
+                                       goto out_cleanup;
                                if (input_register_device(hidinput->input))
                                        goto out_cleanup;
                                hidinput = NULL;
                        }
                }
+       }
 
-       if (hidinput && input_register_device(hidinput->input))
-               goto out_cleanup;
+       if (hidinput && (hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
+           !hidinput_has_been_populated(hidinput)) {
+               /* no need to register an input device not populated */
+               hidinput_cleanup_hidinput(hid, hidinput);
+               hidinput = NULL;
+       }
+
+       if (list_empty(&hid->inputs)) {
+               hid_err(hid, "No inputs registered, leaving\n");
+               goto out_unwind;
+       }
+
+       if (hidinput) {
+               if (drv->input_configured &&
+                   drv->input_configured(hid, hidinput))
+                       goto out_cleanup;
+               if (input_register_device(hidinput->input))
+                       goto out_cleanup;
+       }
 
        return 0;
 
 out_cleanup:
+       list_del(&hidinput->list);
        input_free_device(hidinput->input);
        kfree(hidinput);
 out_unwind:
@@ -798,6 +1412,8 @@ void hidinput_disconnect(struct hid_device *hid)
 {
        struct hid_input *hidinput, *next;
 
+       hidinput_cleanup_battery(hid);
+
        list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
                list_del(&hidinput->list);
                input_unregister_device(hidinput->input);