diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b88ae113d36..af08ce7207d 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1159,7 +1159,7 @@ static bool hid_match_one_id(struct hid_device *hdev, (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-ids.h b/drivers/hid/hid-ids.h index 448e9d89f8d..b8574cddd95 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -125,6 +125,7 @@ #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index c6ee632bfd6..9333d692a78 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -32,6 +32,8 @@ #include #include +#include "hid-ids.h" + #define unk KEY_UNKNOWN static const unsigned char hid_keyboard[256] = { @@ -277,14 +279,38 @@ static enum power_supply_property hidinput_battery_props[] = { POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_STATUS }; +#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_2011_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[2] = {}; switch (prop) { case POWER_SUPPLY_PROP_PRESENT: @@ -293,19 +319,31 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: + ret = dev->hid_get_raw_report(dev, dev->battery_report_id, + buf, sizeof(buf), + dev->battery_report_type); + + if (ret != 2) { + if (ret >= 0) + ret = -EINVAL; + break; + } + if (dev->battery_min < dev->battery_max && - dev->battery_val >= dev->battery_min && - dev->battery_val <= dev->battery_max) - val->intval = (100 * (dev->battery_val - dev->battery_min)) / + buf[1] >= dev->battery_min && + buf[1] <= dev->battery_max) + val->intval = (100 * (buf[1] - dev->battery_min)) / (dev->battery_max - dev->battery_min); - else - ret = -EINVAL; break; case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = dev->name; break; + case POWER_SUPPLY_PROP_STATUS: + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + default: ret = -EINVAL; break; @@ -314,17 +352,22 @@ static int hidinput_get_battery_property(struct power_supply *psy, return ret; } -static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) +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) - return; /* already initialized? */ + goto out; /* already initialized? */ battery->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq); if (battery->name == NULL) - return; + goto out; battery->type = POWER_SUPPLY_TYPE_BATTERY; battery->properties = hidinput_battery_props; @@ -332,8 +375,26 @@ static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) 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) { @@ -341,6 +402,9 @@ static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) kfree(battery->name); battery->name = NULL; } + +out: + return true; } static void hidinput_cleanup_battery(struct hid_device *dev) @@ -353,8 +417,10 @@ static void hidinput_cleanup_battery(struct hid_device *dev) dev->battery.name = NULL; } #else /* !CONFIG_HID_BATTERY_STRENGTH */ -static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) +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) @@ -721,12 +787,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel break; case HID_UP_GENDEVCTRLS: - if ((usage->hid & HID_USAGE) == 0x20) { /* Battery Strength */ - hidinput_setup_battery(device, - field->logical_minimum, - field->logical_maximum); + if (hidinput_setup_battery(device, HID_INPUT_REPORT, field)) goto ignore; - } else + else goto unknown; break; @@ -861,14 +924,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct input = field->hidinput->input; -#ifdef CONFIG_HID_BATTERY_STRENGTH - if (usage->hid == HID_DC_BATTERYSTRENGTH) { - hid->battery_val = value; - hid_dbg(hid, "battery value is %d (range %d-%d)\n", - value, hid->battery_min, hid->battery_max); - return; - } -#endif if (!usage->type) return; @@ -1039,15 +1094,17 @@ static void report_features(struct hid_device *hid) struct hid_report *rep; int i, j; - if (!drv->feature_mapping) - return; - 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++) - for (j = 0; j < rep->field[i]->maxusage; j++) - drv->feature_mapping(hid, rep->field[i], - rep->field[i]->usage + j); + 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); + } } /* diff --git a/include/linux/hid.h b/include/linux/hid.h index 8ac51d6e81e..3a95da60fd3 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -495,7 +495,8 @@ struct hid_device { /* device report descriptor */ struct power_supply battery; __s32 battery_min; __s32 battery_max; - __s32 battery_val; + __s32 battery_report_type; + __s32 battery_report_id; #endif unsigned int status; /* see STAT flags above */ @@ -737,6 +738,8 @@ int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); 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