From 6da7066906e977d42104a859c490f5f9a300488c Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Fri, 28 Oct 2011 18:15:02 +0200 Subject: HID: ignore absolute values which don't fit between logical min and max MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linux should ignore values outside logical min/max range, as they are not meaningful. This is what at least some of other OSes do, and it also makes sense (currently the value gets misinterpreted larger up the stack). Reported-by: Denilson Figueiredo de Sá Tested-by: Denilson Figueiredo de Sá Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index f333139d1a48..b7b0d55c0ede 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -822,6 +822,13 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } + /* Ignore absolute values that are out of bounds */ + if ((usage->type == EV_ABS && (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); -- cgit v1.2.3 From b4b583d4e9a5ff28c4a150bb25a4fff5cd4dfbbd Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 31 Oct 2011 16:26:22 +0100 Subject: HID: be more strict when ignoring out-of-range fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HID 1.11 specification, section 5.10 tells us: HID class devices support the ability to ignore selected fields in a report at run- time. This is accomplished by declaring bit field in a report that is capable of containing a range of values larger than those actually generated by the control. If the host or the device receives an out-of-range value then the current value for the respective control will not be modified. So we shouldn't be restricted to EV_ABS only. Reported-by: Denilson Figueiredo de Sá Tested-by: Denilson Figueiredo de Sá Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index b7b0d55c0ede..6e3252651ce3 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -822,9 +822,8 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } - /* Ignore absolute values that are out of bounds */ - if ((usage->type == EV_ABS && (value < field->logical_minimum || - value > field->logical_maximum))) { + /* Ignore out-of-range values as per HID specification, section 5.10 */ + if (value < field->logical_minimum || value > field->logical_maximum) { dbg_hid("Ignoring out-of-range value %x\n", value); return; } -- cgit v1.2.3 From 4f5ca836bef3dd3eb602152d5d712a513998264e Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 23 Nov 2011 00:49:14 -0800 Subject: HID: hid-input: add support for HID devices reporting Battery Strength Some HID devices, such as my Bluetooth mouse, report their battery strength as an event. Rather than passing it through as a strange absolute input event, this patch registers it with the power_supply subsystem as a battery, so that the device's Battery Strength can be reported to usermode. The battery appears in sysfs names /sys/class/power_supply/hid--battery, and it is a child of the battery-containing device, so it should be clear what it's the battery of. Unfortunately on my current Fedora 16 system, while the battery does appear in the UI, it is listed as a Laptop Battery with 0% charge (since it ignores the "capacity" property of the battery and instead computes it from the "energy*" fields, which we can't supply given the limited information contained within the HID Report). Still, this patch is the first step. Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 6e3252651ce3..2d96b782b203 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -271,6 +271,97 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) return logical_extents / physical_extents; } +#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, +}; + +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; + + switch (prop) { + case POWER_SUPPLY_PROP_PRESENT: + case POWER_SUPPLY_PROP_ONLINE: + val->intval = 1; + break; + + case POWER_SUPPLY_PROP_CAPACITY: + 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)) / + (dev->battery_max - dev->battery_min); + else + ret = -EINVAL; + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = dev->name; + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) +{ + struct power_supply *battery = &dev->battery; + int ret; + + if (battery->name != NULL) + return; /* already initialized? */ + + battery->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq); + if (battery->name == NULL) + return; + + 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; + + dev->battery_min = min; + dev->battery_max = max; + + 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; + } +} + +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 void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) +{ +} + +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) { @@ -629,6 +720,16 @@ 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); + 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) { @@ -760,6 +861,13 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct input = field->hidinput->input; + 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; + } + if (!usage->type) return; @@ -1016,6 +1124,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); -- cgit v1.2.3 From 672007957846c3d556165bab635a9c9b855261fa Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 30 Nov 2011 14:42:04 +0100 Subject: HID: hid-input: fix compile for !HID_BATTERY_STRENGTH As reported by Stephen Rothwell: drivers/hid/hid-input.c: In function 'hidinput_hid_event': drivers/hid/hid-input.c:865:6: error: 'struct hid_device' has no member named 'battery_val' drivers/hid/hid-input.c:866:3: error: 'struct hid_device' has no member named 'battery_min' drivers/hid/hid-input.c:866:3: error: 'struct hid_device' has no member named 'battery_max' Signed-off-by: Jeremy Fitzhardinge Reported-by: Stephen Rothwell Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 2d96b782b203..b9b8c75a6f9a 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -861,13 +861,14 @@ 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; -- cgit v1.2.3 From 4371ea8202e98c8ef77ca887de3b19affbb3498f Mon Sep 17 00:00:00 2001 From: Daniel Kurtz Date: Thu, 17 Nov 2011 19:23:50 +0800 Subject: HID: usbhid: defer LED setting to a workqueue Defer LED setting action to a workqueue. This is more likely to send all LED change events in a single URB. Signed-off-by: Daniel Kurtz Acked-by: Oliver Neukum Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index b9b8c75a6f9a..c6ee632bfd68 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -976,6 +976,48 @@ 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); -- cgit v1.2.3 From c5a92aa3eb7425da68797a820d208edad36551f7 Mon Sep 17 00:00:00 2001 From: Daniel Nicoletti Date: Fri, 2 Dec 2011 03:52:22 -0200 Subject: hid-input: add support for HID devices reporting Battery Strength MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I've sent an email earlier asking for help with a GetFeature code, and now I have a second patch on top of Jeremy's to provide the battery functionality for devices that support reporting it. If I understood correctly when talking to Jeremy he said his device never actually reported the status as an input event (sorry if I didn't understand it correctly), and after reading HID specs I believe it's really because it was meant to be probed, I have an Apple Keyboard and Magic Trackpad both bluetooth batteries operated, so using PacketLogger I saw that Mac OSX always ask the battery status using the so called GetFeature. What my patch does is basically: - store the report id that matches the battery_strength - setup the battery if 0x6.0x20 is found, even if that is reported as a feature (as it was meant to be but only the MagicTrackpad does) - when upower or someone access /sys/class/power_supply/hid-*/capacity it will probe the device and return it's status. It works great for both devices, but I have two concerns: - the report_features function has a duplicated code - it would be nice if it was possible for specific drivers to provide their own probe as there might be some strange devices... (but maybe it's already possible) I've talked to the upower dev and he fixed it to be able to show the right percentage. Here how the uevent file (in /sys/class/power_supply/hid-*/) looks like: POWER_SUPPLY_NAME=hid-00:22:41:D9:18:E7-battery POWER_SUPPLY_PRESENT=1 POWER_SUPPLY_ONLINE=1 POWER_SUPPLY_CAPACITY=66 POWER_SUPPLY_MODEL_NAME=MacAdmin’s keyboard POWER_SUPPLY_STATUS=Discharging POWER_SUPPLY_NAME=hid-70:CD:60:F5:FF:3F-battery POWER_SUPPLY_PRESENT=1 POWER_SUPPLY_ONLINE=1 POWER_SUPPLY_CAPACITY=62 POWER_SUPPLY_MODEL_NAME=nexx’s Trackpad POWER_SUPPLY_STATUS=Discharging Signed-off-by: Daniel Nicoletti --- drivers/hid/hid-input.c | 61 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 15 deletions(-) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index b9b8c75a6f9a..8fac47cf42f1 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -277,6 +277,7 @@ 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 }; static int hidinput_get_battery_property(struct power_supply *psy, @@ -285,6 +286,9 @@ static int hidinput_get_battery_property(struct power_supply *psy, { struct hid_device *dev = container_of(psy, struct hid_device, battery); int ret = 0; + int ret_rep; + __u8 *buf = NULL; + unsigned char report_number = dev->battery_report_id; switch (prop) { case POWER_SUPPLY_PROP_PRESENT: @@ -293,28 +297,45 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: - 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)) / - (dev->battery_max - dev->battery_min); - else + buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + break; + } + + memset(buf, 0, sizeof(buf)); + ret_rep = dev->hid_get_raw_report(dev, report_number, buf, sizeof(buf), HID_FEATURE_REPORT); + if (ret_rep != 2) { ret = -EINVAL; + 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]; 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; } + if (buf) { + kfree(buf); + } return ret; } -static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) +static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, s32 max) { struct power_supply *battery = &dev->battery; int ret; @@ -326,7 +347,7 @@ static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) if (battery->name == NULL) return; - battery->type = POWER_SUPPLY_TYPE_BATTERY; + battery->type = POWER_SUPPLY_TYPE_USB; battery->properties = hidinput_battery_props; battery->num_properties = ARRAY_SIZE(hidinput_battery_props); battery->use_for_apm = 0; @@ -334,6 +355,7 @@ static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) dev->battery_min = min; dev->battery_max = max; + dev->battery_report_id = id; ret = power_supply_register(&dev->dev, battery); if (ret != 0) { @@ -353,7 +375,7 @@ 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 void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, s32 max) { } @@ -723,6 +745,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_UP_GENDEVCTRLS: if ((usage->hid & HID_USAGE) == 0x20) { /* Battery Strength */ hidinput_setup_battery(device, + field->report->id, field->logical_minimum, field->logical_maximum); goto ignore; @@ -997,15 +1020,23 @@ 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 */ + if (((rep->field[i]->usage + j)->hid & HID_USAGE_PAGE) == HID_UP_GENDEVCTRLS && + ((rep->field[i]->usage + j)->hid & HID_USAGE) == 0x20) { + hidinput_setup_battery(hid, + rep->id, + rep->field[i]->logical_minimum, + rep->field[i]->logical_maximum); + } + + if (drv->feature_mapping) + drv->feature_mapping(hid, rep->field[i], + rep->field[i]->usage + j); + } } /* -- cgit v1.2.3 From ef5251993c3597a88b893fe22ee830092dff35f9 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 2 Dec 2011 10:04:04 -0800 Subject: hid-input/battery: remove apparently redundant kmalloc Signed-off-by: Jeremy Fitzhardinge --- drivers/hid/hid-input.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 8fac47cf42f1..b108ce71583f 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -287,7 +287,7 @@ static int hidinput_get_battery_property(struct power_supply *psy, struct hid_device *dev = container_of(psy, struct hid_device, battery); int ret = 0; int ret_rep; - __u8 *buf = NULL; + __u8 buf[2] = {}; unsigned char report_number = dev->battery_report_id; switch (prop) { @@ -297,14 +297,8 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: - buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - break; - } - - memset(buf, 0, sizeof(buf)); - ret_rep = dev->hid_get_raw_report(dev, report_number, buf, sizeof(buf), HID_FEATURE_REPORT); + ret_rep = dev->hid_get_raw_report(dev, report_number, + buf, sizeof(buf), HID_FEATURE_REPORT); if (ret_rep != 2) { ret = -EINVAL; break; @@ -329,9 +323,6 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; } - if (buf) { - kfree(buf); - } return ret; } -- cgit v1.2.3 From bbc21cfd55858d7c3e55bfaa91fa934b0b13ad4d Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 2 Dec 2011 11:12:36 -0800 Subject: 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 --- drivers/hid/hid-input.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index b108ce71583f..69dec476883a 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] = { @@ -280,6 +282,28 @@ static enum power_supply_property hidinput_battery_props[] = { 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 @@ static int hidinput_get_battery_property(struct power_supply *psy, 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 @@ static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, { struct power_supply *battery = &dev->battery; int ret; + unsigned quirks; if (battery->name != NULL) return; /* already initialized? */ @@ -344,6 +370,13 @@ static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, 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; -- cgit v1.2.3 From fb8ac91b4dccbdda0ad51d499079d05143783ba4 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 2 Dec 2011 11:18:45 -0800 Subject: hid-input/battery: deal with both FEATURE and INPUT report batteries Some devices seem to report batteries as FEATUREs, others as INPUTs. Signed-off-by: Jeremy Fitzhardinge --- drivers/hid/hid-input.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 69dec476883a..f5c3efcdcb02 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -312,7 +312,6 @@ static int hidinput_get_battery_property(struct power_supply *psy, int ret = 0; int ret_rep; __u8 buf[2] = {}; - unsigned char report_number = dev->battery_report_id; switch (prop) { case POWER_SUPPLY_PROP_PRESENT: @@ -321,8 +320,9 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: - ret_rep = dev->hid_get_raw_report(dev, report_number, - buf, sizeof(buf), HID_FEATURE_REPORT); + ret_rep = dev->hid_get_raw_report(dev, dev->battery_report_id, + buf, sizeof(buf), + dev->battery_report_type); if (ret_rep != 2) { ret = -EINVAL; break; @@ -351,7 +351,9 @@ static int hidinput_get_battery_property(struct power_supply *psy, return ret; } -static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, s32 max) +static void hidinput_setup_battery(struct hid_device *dev, + unsigned report_type, unsigned report_id, + s32 min, s32 max) { struct power_supply *battery = &dev->battery; int ret; @@ -379,7 +381,8 @@ static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, dev->battery_min = min; dev->battery_max = max; - dev->battery_report_id = id; + dev->battery_report_type = report_type; + dev->battery_report_id = report_id; ret = power_supply_register(&dev->dev, battery); if (ret != 0) { @@ -399,7 +402,9 @@ 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, unsigned id, s32 min, s32 max) +static void hidinput_setup_battery(struct hid_device *dev, + unsigned report_type, unsigned report_id, + s32 min, s32 max) { } @@ -769,6 +774,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_UP_GENDEVCTRLS: if ((usage->hid & HID_USAGE) == 0x20) { /* Battery Strength */ hidinput_setup_battery(device, + HID_INPUT_REPORT, field->report->id, field->logical_minimum, field->logical_maximum); @@ -1052,7 +1058,7 @@ static void report_features(struct hid_device *hid) if (((rep->field[i]->usage + j)->hid & HID_USAGE_PAGE) == HID_UP_GENDEVCTRLS && ((rep->field[i]->usage + j)->hid & HID_USAGE) == 0x20) { hidinput_setup_battery(hid, - rep->id, + HID_FEATURE_REPORT, rep->id, rep->field[i]->logical_minimum, rep->field[i]->logical_maximum); } -- cgit v1.2.3 From 2f2e3f6d7a24e7fceb0fef514767025cddb64dc2 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 2 Dec 2011 19:05:07 -0800 Subject: hid-input/battery: make the battery setup common for INPUTs and FEATUREs Signed-off-by: Jeremy Fitzhardinge --- drivers/hid/hid-input.c | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index f5c3efcdcb02..771ff5df5742 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -351,20 +351,22 @@ static int hidinput_get_battery_property(struct power_supply *psy, return ret; } -static void hidinput_setup_battery(struct hid_device *dev, - unsigned report_type, unsigned report_id, - 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_USB; battery->properties = hidinput_battery_props; @@ -374,6 +376,9 @@ static void hidinput_setup_battery(struct hid_device *dev, quirks = find_battery_quirk(dev); + min = field->logical_minimum; + max = field->logical_maximum; + if (quirks & HID_BATTERY_QUIRK_PERCENT) { min = 0; max = 100; @@ -382,7 +387,7 @@ static void hidinput_setup_battery(struct hid_device *dev, dev->battery_min = min; dev->battery_max = max; dev->battery_report_type = report_type; - dev->battery_report_id = report_id; + dev->battery_report_id = field->report->id; ret = power_supply_register(&dev->dev, battery); if (ret != 0) { @@ -390,6 +395,9 @@ static void hidinput_setup_battery(struct hid_device *dev, kfree(battery->name); battery->name = NULL; } + +out: + return true; } static void hidinput_cleanup_battery(struct hid_device *dev) @@ -402,10 +410,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, - unsigned report_type, unsigned report_id, - 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) @@ -772,14 +780,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, - HID_INPUT_REPORT, - field->report->id, - field->logical_minimum, - field->logical_maximum); + if (hidinput_setup_battery(device, HID_INPUT_REPORT, field)) goto ignore; - } else + else goto unknown; break; @@ -1055,13 +1058,7 @@ static void report_features(struct hid_device *hid) for (i = 0; i < rep->maxfield; i++) for (j = 0; j < rep->field[i]->maxusage; j++) { /* Verify if Battery Strength feature is available */ - if (((rep->field[i]->usage + j)->hid & HID_USAGE_PAGE) == HID_UP_GENDEVCTRLS && - ((rep->field[i]->usage + j)->hid & HID_USAGE) == 0x20) { - hidinput_setup_battery(hid, - HID_FEATURE_REPORT, rep->id, - rep->field[i]->logical_minimum, - rep->field[i]->logical_maximum); - } + hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]); if (drv->feature_mapping) drv->feature_mapping(hid, rep->field[i], -- cgit v1.2.3 From b3ca3839f344aa469e6f53c8bbb633e5ab9b96c8 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 2 Dec 2011 19:06:10 -0800 Subject: hid-input/battery: power-supply type really *is* a battery It just isn't a battery which is powering the computer. upower needs a more nuanced understanding of this. Signed-off-by: Jeremy Fitzhardinge --- drivers/hid/hid-input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 771ff5df5742..ceade58b8027 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -368,7 +368,7 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, if (battery->name == NULL) goto out; - battery->type = POWER_SUPPLY_TYPE_USB; + battery->type = POWER_SUPPLY_TYPE_BATTERY; battery->properties = hidinput_battery_props; battery->num_properties = ARRAY_SIZE(hidinput_battery_props); battery->use_for_apm = 0; -- cgit v1.2.3 From ce63920b395f1476e2d28cca16a56919289f0b62 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 2 Dec 2011 21:57:50 -0800 Subject: hid-input/battery: remove battery_val hidinput_get_battery_property() now directly polls the device for the current battery strength, so there's no need for battery_val, or the code to set it on the input event path. Signed-off-by: Jeremy Fitzhardinge --- drivers/hid/hid-input.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index ceade58b8027..48785db10e87 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -917,14 +917,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; -- cgit v1.2.3 From 652aa6a9ac4a5f8d3e1fa3f6466646519e83c01e Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 9 Dec 2011 00:10:28 -0800 Subject: hid-input/battery: add FEATURE quirk Apple keyboards require a FEATURE report to query the battery state, even though they list as an input. Without this, it returns an error. Signed-off-by: Jeremy Fitzhardinge --- drivers/hid/hid-input.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 48785db10e87..0f1250d10ea1 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -283,12 +283,12 @@ static enum power_supply_property hidinput_battery_props[] = { }; #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_MAGICMOUSE), - HID_BATTERY_QUIRK_PERCENT }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD), - HID_BATTERY_QUIRK_PERCENT }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, {} }; @@ -310,7 +310,6 @@ static int hidinput_get_battery_property(struct power_supply *psy, { struct hid_device *dev = container_of(psy, struct hid_device, battery); int ret = 0; - int ret_rep; __u8 buf[2] = {}; switch (prop) { @@ -320,11 +319,13 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: - ret_rep = dev->hid_get_raw_report(dev, dev->battery_report_id, - buf, sizeof(buf), - dev->battery_report_type); - if (ret_rep != 2) { - ret = -EINVAL; + 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; } @@ -376,6 +377,9 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, 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; @@ -384,6 +388,9 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, 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; -- cgit v1.2.3