hid-input/battery: add quirks for battery

Some devices always report percentage, despite having 0/255 as their
min/max, so add a quirk for them.

Signed-off-by: Jeremy Fitzhardinge <jeremy@goop.org>
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index c0ef2b4..aa4a30b 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1157,7 +1157,7 @@
 		(id->product == HID_ANY_ID || id->product == hdev->product);
 }
 
-static const struct hid_device_id *hid_match_id(struct hid_device *hdev,
+const struct hid_device_id *hid_match_id(struct hid_device *hdev,
 		const struct hid_device_id *id)
 {
 	for (; id->bus; id++)
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index b108ce7..69dec47 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -32,6 +32,8 @@
 #include <linux/hid.h>
 #include <linux/hid-debug.h>
 
+#include "hid-ids.h"
+
 #define unk	KEY_UNKNOWN
 
 static const unsigned char hid_keyboard[256] = {
@@ -280,6 +282,28 @@
 	POWER_SUPPLY_PROP_STATUS
 };
 
+#define HID_BATTERY_QUIRK_PERCENT	(1 << 0) /* always reports percent */
+
+static const struct hid_device_id hid_battery_quirks[] = {
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE),
+	  HID_BATTERY_QUIRK_PERCENT },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD),
+	  HID_BATTERY_QUIRK_PERCENT },
+	{}
+};
+
+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)
@@ -304,10 +328,11 @@
 			break;
 		}
 
-		/* store the returned value */
-		/* I'm not calculating this using the logical_minimum and maximum */
-		/* because my device returns 0-100 even though the min and max are 0-255 */
-		val->intval = buf[1];
+		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);
 		break;
 
 	case POWER_SUPPLY_PROP_MODEL_NAME:
@@ -330,6 +355,7 @@
 {
 	struct power_supply *battery = &dev->battery;
 	int ret;
+	unsigned quirks;
 
 	if (battery->name != NULL)
 		return;		/* already initialized? */
@@ -344,6 +370,13 @@
 	battery->use_for_apm = 0;
 	battery->get_property = hidinput_get_battery_property;
 
+	quirks = find_battery_quirk(dev);
+
+	if (quirks & HID_BATTERY_QUIRK_PERCENT) {
+		min = 0;
+		max = 100;
+	}
+
 	dev->battery_min = min;
 	dev->battery_max = max;
 	dev->battery_report_id = id;
diff --git a/include/linux/hid.h b/include/linux/hid.h
index b5df198..fa772c8 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -735,6 +735,8 @@
 int hid_check_keys_pressed(struct hid_device *hid);
 int hid_connect(struct hid_device *hid, unsigned int connect_mask);
 void hid_disconnect(struct hid_device *hid);
+const struct hid_device_id *hid_match_id(struct hid_device *hdev,
+					 const struct hid_device_id *id);
 
 /**
  * hid_map_usage - map usage input bits