aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorDaniel Nicoletti <dantti12@gmail.com>2011-12-02 00:52:22 -0500
committerJeremy Fitzhardinge <jeremy@goop.org>2012-01-08 02:30:34 -0500
commitc5a92aa3eb7425da68797a820d208edad36551f7 (patch)
tree92d2e8304ce9729c8feb3eeb4689cc8cd0561df5 /drivers/hid
parent672007957846c3d556165bab635a9c9b855261fa (diff)
hid-input: add support for HID devices reporting Battery Strength
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 <dantti12@gmail.com>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/hid-input.c61
1 files changed, 46 insertions, 15 deletions
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[] = {
277 POWER_SUPPLY_PROP_ONLINE, 277 POWER_SUPPLY_PROP_ONLINE,
278 POWER_SUPPLY_PROP_CAPACITY, 278 POWER_SUPPLY_PROP_CAPACITY,
279 POWER_SUPPLY_PROP_MODEL_NAME, 279 POWER_SUPPLY_PROP_MODEL_NAME,
280 POWER_SUPPLY_PROP_STATUS
280}; 281};
281 282
282static int hidinput_get_battery_property(struct power_supply *psy, 283static int hidinput_get_battery_property(struct power_supply *psy,
@@ -285,6 +286,9 @@ static int hidinput_get_battery_property(struct power_supply *psy,
285{ 286{
286 struct hid_device *dev = container_of(psy, struct hid_device, battery); 287 struct hid_device *dev = container_of(psy, struct hid_device, battery);
287 int ret = 0; 288 int ret = 0;
289 int ret_rep;
290 __u8 *buf = NULL;
291 unsigned char report_number = dev->battery_report_id;
288 292
289 switch (prop) { 293 switch (prop) {
290 case POWER_SUPPLY_PROP_PRESENT: 294 case POWER_SUPPLY_PROP_PRESENT:
@@ -293,28 +297,45 @@ static int hidinput_get_battery_property(struct power_supply *psy,
293 break; 297 break;
294 298
295 case POWER_SUPPLY_PROP_CAPACITY: 299 case POWER_SUPPLY_PROP_CAPACITY:
296 if (dev->battery_min < dev->battery_max && 300 buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL);
297 dev->battery_val >= dev->battery_min && 301 if (!buf) {
298 dev->battery_val <= dev->battery_max) 302 ret = -ENOMEM;
299 val->intval = (100 * (dev->battery_val - dev->battery_min)) / 303 break;
300 (dev->battery_max - dev->battery_min); 304 }
301 else 305
306 memset(buf, 0, sizeof(buf));
307 ret_rep = dev->hid_get_raw_report(dev, report_number, buf, sizeof(buf), HID_FEATURE_REPORT);
308 if (ret_rep != 2) {
302 ret = -EINVAL; 309 ret = -EINVAL;
310 break;
311 }
312
313 /* store the returned value */
314 /* I'm not calculating this using the logical_minimum and maximum */
315 /* because my device returns 0-100 even though the min and max are 0-255 */
316 val->intval = buf[1];
303 break; 317 break;
304 318
305 case POWER_SUPPLY_PROP_MODEL_NAME: 319 case POWER_SUPPLY_PROP_MODEL_NAME:
306 val->strval = dev->name; 320 val->strval = dev->name;
307 break; 321 break;
308 322
323 case POWER_SUPPLY_PROP_STATUS:
324 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
325 break;
326
309 default: 327 default:
310 ret = -EINVAL; 328 ret = -EINVAL;
311 break; 329 break;
312 } 330 }
313 331
332 if (buf) {
333 kfree(buf);
334 }
314 return ret; 335 return ret;
315} 336}
316 337
317static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) 338static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, s32 max)
318{ 339{
319 struct power_supply *battery = &dev->battery; 340 struct power_supply *battery = &dev->battery;
320 int ret; 341 int ret;
@@ -326,7 +347,7 @@ static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max)
326 if (battery->name == NULL) 347 if (battery->name == NULL)
327 return; 348 return;
328 349
329 battery->type = POWER_SUPPLY_TYPE_BATTERY; 350 battery->type = POWER_SUPPLY_TYPE_USB;
330 battery->properties = hidinput_battery_props; 351 battery->properties = hidinput_battery_props;
331 battery->num_properties = ARRAY_SIZE(hidinput_battery_props); 352 battery->num_properties = ARRAY_SIZE(hidinput_battery_props);
332 battery->use_for_apm = 0; 353 battery->use_for_apm = 0;
@@ -334,6 +355,7 @@ static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max)
334 355
335 dev->battery_min = min; 356 dev->battery_min = min;
336 dev->battery_max = max; 357 dev->battery_max = max;
358 dev->battery_report_id = id;
337 359
338 ret = power_supply_register(&dev->dev, battery); 360 ret = power_supply_register(&dev->dev, battery);
339 if (ret != 0) { 361 if (ret != 0) {
@@ -353,7 +375,7 @@ static void hidinput_cleanup_battery(struct hid_device *dev)
353 dev->battery.name = NULL; 375 dev->battery.name = NULL;
354} 376}
355#else /* !CONFIG_HID_BATTERY_STRENGTH */ 377#else /* !CONFIG_HID_BATTERY_STRENGTH */
356static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) 378static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, s32 max)
357{ 379{
358} 380}
359 381
@@ -723,6 +745,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
723 case HID_UP_GENDEVCTRLS: 745 case HID_UP_GENDEVCTRLS:
724 if ((usage->hid & HID_USAGE) == 0x20) { /* Battery Strength */ 746 if ((usage->hid & HID_USAGE) == 0x20) { /* Battery Strength */
725 hidinput_setup_battery(device, 747 hidinput_setup_battery(device,
748 field->report->id,
726 field->logical_minimum, 749 field->logical_minimum,
727 field->logical_maximum); 750 field->logical_maximum);
728 goto ignore; 751 goto ignore;
@@ -997,15 +1020,23 @@ static void report_features(struct hid_device *hid)
997 struct hid_report *rep; 1020 struct hid_report *rep;
998 int i, j; 1021 int i, j;
999 1022
1000 if (!drv->feature_mapping)
1001 return;
1002
1003 rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; 1023 rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
1004 list_for_each_entry(rep, &rep_enum->report_list, list) 1024 list_for_each_entry(rep, &rep_enum->report_list, list)
1005 for (i = 0; i < rep->maxfield; i++) 1025 for (i = 0; i < rep->maxfield; i++)
1006 for (j = 0; j < rep->field[i]->maxusage; j++) 1026 for (j = 0; j < rep->field[i]->maxusage; j++) {
1007 drv->feature_mapping(hid, rep->field[i], 1027 /* Verify if Battery Strength feature is available */
1008 rep->field[i]->usage + j); 1028 if (((rep->field[i]->usage + j)->hid & HID_USAGE_PAGE) == HID_UP_GENDEVCTRLS &&
1029 ((rep->field[i]->usage + j)->hid & HID_USAGE) == 0x20) {
1030 hidinput_setup_battery(hid,
1031 rep->id,
1032 rep->field[i]->logical_minimum,
1033 rep->field[i]->logical_maximum);
1034 }
1035
1036 if (drv->feature_mapping)
1037 drv->feature_mapping(hid, rep->field[i],
1038 rep->field[i]->usage + j);
1039 }
1009} 1040}
1010 1041
1011/* 1042/*