diff options
-rw-r--r-- | drivers/hid/hid-input.c | 184 | ||||
-rw-r--r-- | include/linux/hid.h | 2 |
2 files changed, 127 insertions, 59 deletions
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 2742d0b840c6..0e6e8fa94046 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -339,13 +339,45 @@ static unsigned find_battery_quirk(struct hid_device *hdev) return quirks; } +static int hidinput_scale_battery_capacity(struct hid_device *dev, + int value) +{ + if (dev->battery_min < dev->battery_max && + value >= dev->battery_min && value <= dev->battery_max) + value = ((value - dev->battery_min) * 100) / + (dev->battery_max - dev->battery_min); + + return value; +} + +static int hidinput_query_battery_capacity(struct hid_device *dev) +{ + u8 *buf; + int ret; + + buf = kmalloc(2, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2, + dev->battery_report_type, HID_REQ_GET_REPORT); + if (ret != 2) { + kfree(buf); + return -ENODATA; + } + + ret = hidinput_scale_battery_capacity(dev, buf[1]); + kfree(buf); + return ret; +} + static int hidinput_get_battery_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { struct hid_device *dev = power_supply_get_drvdata(psy); + int value; int ret = 0; - __u8 *buf; switch (prop) { case POWER_SUPPLY_PROP_PRESENT: @@ -354,29 +386,15 @@ 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; - } - ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2, - dev->battery_report_type, - HID_REQ_GET_REPORT); - - if (ret != 2) { - ret = -ENODATA; - kfree(buf); - break; + if (dev->battery_report_type == HID_FEATURE_REPORT) { + value = hidinput_query_battery_capacity(dev); + if (value < 0) + return value; + } else { + value = dev->battery_capacity; } - ret = 0; - 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); - kfree(buf); + val->intval = value; break; case POWER_SUPPLY_PROP_MODEL_NAME: @@ -384,7 +402,22 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_STATUS: - val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + if (!dev->battery_reported && + dev->battery_report_type == HID_FEATURE_REPORT) { + value = hidinput_query_battery_capacity(dev); + if (value < 0) + return value; + + dev->battery_capacity = value; + dev->battery_reported = true; + } + + if (!dev->battery_reported) + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + else if (dev->battery_capacity == 100) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; case POWER_SUPPLY_PROP_SCOPE: @@ -399,18 +432,16 @@ static int hidinput_get_battery_property(struct power_supply *psy, return ret; } -static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field) +static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field) { - struct power_supply_desc *psy_desc = NULL; + struct power_supply_desc *psy_desc; struct power_supply_config psy_cfg = { .drv_data = dev, }; unsigned quirks; s32 min, max; + int error; - if (field->usage->hid != HID_DC_BATTERYSTRENGTH) - return false; /* no match */ - - if (dev->battery != NULL) - goto out; /* already initialized? */ + if (dev->battery) + return 0; /* already initialized? */ quirks = find_battery_quirk(dev); @@ -418,16 +449,16 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, dev->bus, dev->vendor, dev->product, dev->version, quirks); if (quirks & HID_BATTERY_QUIRK_IGNORE) - goto out; + return 0; psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL); - if (psy_desc == NULL) - goto out; + if (!psy_desc) + return -ENOMEM; psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq); - if (psy_desc->name == NULL) { - kfree(psy_desc); - goto out; + if (!psy_desc->name) { + error = -ENOMEM; + goto err_free_mem; } psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; @@ -454,17 +485,20 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg); if (IS_ERR(dev->battery)) { - hid_warn(dev, "can't register power supply: %ld\n", - PTR_ERR(dev->battery)); - kfree(psy_desc->name); - kfree(psy_desc); - dev->battery = NULL; - } else { - power_supply_powers(dev->battery, &dev->dev); + error = PTR_ERR(dev->battery); + hid_warn(dev, "can't register power supply: %d\n", error); + goto err_free_name; } -out: - return true; + power_supply_powers(dev->battery, &dev->dev); + return 0; + +err_free_name: + kfree(psy_desc->name); +err_free_mem: + kfree(psy_desc); + dev->battery = NULL; + return error; } static void hidinput_cleanup_battery(struct hid_device *dev) @@ -480,16 +514,33 @@ static void hidinput_cleanup_battery(struct hid_device *dev) kfree(psy_desc); dev->battery = NULL; } + +static void hidinput_update_battery(struct hid_device *dev, int value) +{ + if (!dev->battery) + return; + + if (value == 0 || value < dev->battery_min || value > dev->battery_max) + return; + + dev->battery_capacity = hidinput_scale_battery_capacity(dev, value); + dev->battery_reported = true; + power_supply_changed(dev->battery); +} #else /* !CONFIG_HID_BATTERY_STRENGTH */ -static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, - struct hid_field *field) +static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, + struct hid_field *field) { - return false; + return 0; } static void hidinput_cleanup_battery(struct hid_device *dev) { } + +static void hidinput_update_battery(struct hid_device *dev, int value) +{ +} #endif /* CONFIG_HID_BATTERY_STRENGTH */ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, @@ -691,6 +742,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } break; + case 0x3b: /* Battery Strength */ + hidinput_setup_battery(device, HID_INPUT_REPORT, field); + usage->type = EV_PWR; + goto ignore; + case 0x3c: /* Invert */ map_key_clear(BTN_TOOL_RUBBER); break; @@ -931,11 +987,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel break; case HID_UP_GENDEVCTRLS: - if (hidinput_setup_battery(device, HID_INPUT_REPORT, field)) + switch (usage->hid) { + case HID_DC_BATTERYSTRENGTH: + hidinput_setup_battery(device, HID_INPUT_REPORT, field); + usage->type = EV_PWR; goto ignore; - else - goto unknown; - break; + } + goto unknown; case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ set_bit(EV_REP, input->evbit); @@ -1013,7 +1071,6 @@ mapped: if (usage->code > max) goto ignore; - if (usage->type == EV_ABS) { int a = field->logical_minimum; @@ -1072,14 +1129,19 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct struct input_dev *input; unsigned *quirks = &hid->quirks; - if (!field->hidinput) + if (!usage->type) return; - input = field->hidinput->input; + if (usage->type == EV_PWR) { + hidinput_update_battery(hid, value); + return; + } - if (!usage->type) + if (!field->hidinput) return; + input = field->hidinput->input; + if (usage->hat_min < usage->hat_max || usage->hat_dir) { int hat_dir = usage->hat_dir; if (!hat_dir) @@ -1356,6 +1418,7 @@ static void report_features(struct hid_device *hid) struct hid_driver *drv = hid->driver; struct hid_report_enum *rep_enum; struct hid_report *rep; + struct hid_usage *usage; int i, j; rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; @@ -1366,12 +1429,15 @@ static void report_features(struct hid_device *hid) continue; for (j = 0; j < rep->field[i]->maxusage; j++) { + usage = &rep->field[i]->usage[j]; + /* Verify if Battery Strength feature is available */ - hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]); + if (usage->hid == HID_DC_BATTERYSTRENGTH) + hidinput_setup_battery(hid, HID_FEATURE_REPORT, + rep->field[i]); if (drv->feature_mapping) - drv->feature_mapping(hid, rep->field[i], - rep->field[i]->usage + j); + drv->feature_mapping(hid, rep->field[i], usage); } } } diff --git a/include/linux/hid.h b/include/linux/hid.h index 2cdcf713ef51..95a428fe8956 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -522,10 +522,12 @@ struct hid_device { /* device report descriptor */ * battery is non-NULL. */ struct power_supply *battery; + __s32 battery_capacity; __s32 battery_min; __s32 battery_max; __s32 battery_report_type; __s32 battery_report_id; + bool battery_reported; #endif unsigned int status; /* see STAT flags above */ |