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-59-g8ed1b 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-59-g8ed1b 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/Kconfig | 5 +++ drivers/hid/hid-input.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/hid.h | 16 +++++++ 3 files changed, 131 insertions(+) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 22a4a051f221..3a97f1fab243 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -31,6 +31,11 @@ config HID If unsure, say Y. +config HID_BATTERY_STRENGTH + bool + depends on POWER_SUPPLY + default y + config HIDRAW bool "/dev/hidraw raw HID device support" depends on HID 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); diff --git a/include/linux/hid.h b/include/linux/hid.h index deed5f9a1e1c..7f344c3da767 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -72,6 +72,7 @@ #include #include #include +#include /* * We parse each description item into this structure. Short items data @@ -190,6 +191,7 @@ struct hid_item { #define HID_UP_UNDEFINED 0x00000000 #define HID_UP_GENDESK 0x00010000 #define HID_UP_SIMULATION 0x00020000 +#define HID_UP_GENDEVCTRLS 0x00060000 #define HID_UP_KEYBOARD 0x00070000 #define HID_UP_LED 0x00080000 #define HID_UP_BUTTON 0x00090000 @@ -239,6 +241,8 @@ struct hid_item { #define HID_GD_RIGHT 0x00010092 #define HID_GD_LEFT 0x00010093 +#define HID_DC_BATTERYSTRENGTH 0x00060020 + #define HID_DG_DIGITIZER 0x000d0001 #define HID_DG_PEN 0x000d0002 #define HID_DG_LIGHTPEN 0x000d0003 @@ -482,6 +486,18 @@ struct hid_device { /* device report descriptor */ struct hid_driver *driver; struct hid_ll_driver *ll_driver; +#ifdef CONFIG_HID_BATTERY_STRENGTH + /* + * Power supply information for HID devices which report + * battery strength. power_supply is registered iff + * battery.name is non-NULL. + */ + struct power_supply battery; + __s32 battery_min; + __s32 battery_max; + __s32 battery_val; +#endif + unsigned int status; /* see STAT flags above */ unsigned claimed; /* Claimed by hidinput, hiddev? */ unsigned quirks; /* Various quirks the device can pull on us */ -- cgit v1.2.3-59-g8ed1b 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-59-g8ed1b 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 ++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-core.c | 47 +++++++++++++++++++++++++++++++++---------- drivers/hid/usbhid/usbhid.h | 2 ++ include/linux/hid.h | 2 ++ 4 files changed, 82 insertions(+), 11 deletions(-) (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); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 719f6b02fab1..5bf91dbad59d 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -602,6 +602,30 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns } EXPORT_SYMBOL_GPL(usbhid_submit_report); +/* Workqueue routine to send requests to change LEDs */ +static void hid_led(struct work_struct *work) +{ + struct usbhid_device *usbhid = + container_of(work, struct usbhid_device, led_work); + struct hid_device *hid = usbhid->hid; + struct hid_field *field; + unsigned long flags; + + field = hidinput_get_led_field(hid); + if (!field) { + hid_warn(hid, "LED event field not found\n"); + return; + } + + spin_lock_irqsave(&usbhid->lock, flags); + if (!test_bit(HID_DISCONNECTED, &usbhid->iofl)) { + usbhid->ledcount = hidinput_count_leds(hid); + hid_dbg(usbhid->hid, "New ledcount = %u\n", usbhid->ledcount); + __usbhid_submit_report(hid, field->report, USB_DIR_OUT); + } + spin_unlock_irqrestore(&usbhid->lock, flags); +} + static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct hid_device *hid = input_get_drvdata(dev); @@ -621,17 +645,15 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un return -1; } + spin_lock_irqsave(&usbhid->lock, flags); hid_set_field(field, offset, value); - if (value) { - spin_lock_irqsave(&usbhid->lock, flags); - usbhid->ledcount++; - spin_unlock_irqrestore(&usbhid->lock, flags); - } else { - spin_lock_irqsave(&usbhid->lock, flags); - usbhid->ledcount--; - spin_unlock_irqrestore(&usbhid->lock, flags); - } - usbhid_submit_report(hid, field->report, USB_DIR_OUT); + spin_unlock_irqrestore(&usbhid->lock, flags); + + /* + * Defer performing requested LED action. + * This is more likely gather all LED changes into a single URB. + */ + schedule_work(&usbhid->led_work); return 0; } @@ -1126,7 +1148,7 @@ static void usbhid_stop(struct hid_device *hid) return; clear_bit(HID_STARTED, &usbhid->iofl); - spin_lock_irq(&usbhid->lock); /* Sync with error handler */ + spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */ set_bit(HID_DISCONNECTED, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); usb_kill_urb(usbhid->urbin); @@ -1260,6 +1282,8 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); spin_lock_init(&usbhid->lock); + INIT_WORK(&usbhid->led_work, hid_led); + ret = hid_add_device(hid); if (ret) { if (ret != -ENODEV) @@ -1292,6 +1316,7 @@ static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid) { del_timer_sync(&usbhid->io_retry); cancel_work_sync(&usbhid->reset_work); + cancel_work_sync(&usbhid->led_work); } static void hid_cease_io(struct usbhid_device *usbhid) diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 2d8957c11d2d..cb8f703efde5 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -96,6 +96,8 @@ struct usbhid_device { struct work_struct reset_work; /* Task context for resets */ wait_queue_head_t wait; /* For sleeping */ int ledcount; /* counting the number of active leds */ + + struct work_struct led_work; /* Task context for setting LEDs */ }; #define hid_to_usb_dev(hid_dev) \ diff --git a/include/linux/hid.h b/include/linux/hid.h index 7f344c3da767..999a54c72b20 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -727,6 +727,8 @@ extern void hidinput_disconnect(struct hid_device *); int hid_set_field(struct hid_field *, unsigned, __s32); int hid_input_report(struct hid_device *, int type, u8 *, int, int); int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field); +struct hid_field *hidinput_get_led_field(struct hid_device *hid); +unsigned int hidinput_count_leds(struct hid_device *hid); void hid_output_report(struct hid_report *report, __u8 *data); struct hid_device *hid_allocate_device(void); struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id); -- cgit v1.2.3-59-g8ed1b 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 +++++++++++++++++++++++++++++++++++++------------ include/linux/hid.h | 1 + 2 files changed, 47 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); + } } /* diff --git a/include/linux/hid.h b/include/linux/hid.h index 7f344c3da767..b5df198d87a5 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -496,6 +496,7 @@ struct hid_device { /* device report descriptor */ __s32 battery_min; __s32 battery_max; __s32 battery_val; + __s32 battery_report_id; #endif unsigned int status; /* see STAT flags above */ -- cgit v1.2.3-59-g8ed1b 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-59-g8ed1b 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-core.c | 2 +- drivers/hid/hid-input.c | 41 +++++++++++++++++++++++++++++++++++++---- include/linux/hid.h | 2 ++ 3 files changed, 40 insertions(+), 5 deletions(-) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index c0ef2b49a00c..aa4a30b7c6af 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1157,7 +1157,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-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; diff --git a/include/linux/hid.h b/include/linux/hid.h index b5df198d87a5..fa772c86fa2c 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -735,6 +735,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 -- cgit v1.2.3-59-g8ed1b 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 +++++++++++++------- include/linux/hid.h | 1 + 2 files changed, 14 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); } diff --git a/include/linux/hid.h b/include/linux/hid.h index fa772c86fa2c..9351d3d1d089 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -496,6 +496,7 @@ struct hid_device { /* device report descriptor */ __s32 battery_min; __s32 battery_max; __s32 battery_val; + __s32 battery_report_type; __s32 battery_report_id; #endif -- cgit v1.2.3-59-g8ed1b 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-59-g8ed1b 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-59-g8ed1b 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 -------- include/linux/hid.h | 1 - 2 files changed, 9 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; diff --git a/include/linux/hid.h b/include/linux/hid.h index 9351d3d1d089..0e76f0ca110a 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -495,7 +495,6 @@ 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 -- cgit v1.2.3-59-g8ed1b 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-ids.h | 1 + drivers/hid/hid-input.c | 27 +++++++++++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 06ce996b8b65..b63beb8e67d5 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -124,6 +124,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 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-59-g8ed1b