summaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorJeremy Fitzhardinge <jeremy@goop.org>2011-11-23 00:49:14 -0800
committerJiri Kosina <jkosina@suse.cz>2011-11-28 11:10:22 +0100
commit4f5ca836bef3dd3eb602152d5d712a513998264e (patch)
tree37c1c2ba43fdcadac3e7627b57999ebb0c71bee4 /drivers/hid
parenta2b2c20ba2f6e22c065f401d63f7f883779cf642 (diff)
downloadlinux-4f5ca836bef3dd3eb602152d5d712a513998264e.tar.bz2
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-<UNIQ>-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 <jeremy@goop.org> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/Kconfig5
-rw-r--r--drivers/hid/hid-input.c110
2 files changed, 115 insertions, 0 deletions
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);