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 --- include/linux/hid.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux/hid.h') 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 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 'include/linux/hid.h') 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 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 'include/linux/hid.h') 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 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 'include/linux/hid.h') 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