Loading drivers/hid/hid-input.c +135 −62 Original line number Diff line number Diff line Loading @@ -303,6 +303,7 @@ 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 */ #define HID_BATTERY_QUIRK_IGNORE (1 << 2) /* completely ignore the battery */ static const struct hid_device_id hid_battery_quirks[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, Loading @@ -320,6 +321,9 @@ static const struct hid_device_id hid_battery_quirks[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084), HID_BATTERY_QUIRK_IGNORE }, {} }; Loading @@ -335,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: Loading @@ -350,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: Loading @@ -380,6 +402,21 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_STATUS: 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; Loading @@ -395,27 +432,33 @@ 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) return 0; /* already initialized? */ if (dev->battery != NULL) goto out; /* already initialized? */ 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); if (quirks & HID_BATTERY_QUIRK_IGNORE) 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; Loading @@ -424,11 +467,6 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, psy_desc->use_for_apm = 0; psy_desc->get_property = hidinput_get_battery_property; 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; Loading @@ -447,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)); error = PTR_ERR(dev->battery); hid_warn(dev, "can't register power supply: %d\n", error); goto err_free_name; } 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; } else { power_supply_powers(dev->battery, &dev->dev); } out: return true; return error; } static void hidinput_cleanup_battery(struct hid_device *dev) Loading @@ -473,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, 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, Loading Loading @@ -684,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; Loading Loading @@ -924,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; case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ set_bit(EV_REP, input->evbit); Loading Loading @@ -1006,7 +1071,6 @@ mapped: if (usage->code > max) goto ignore; if (usage->type == EV_ABS) { int a = field->logical_minimum; Loading Loading @@ -1065,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) Loading Loading @@ -1349,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]; Loading @@ -1359,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); } } } Loading include/linux/hid.h +2 −0 Original line number Diff line number Diff line Loading @@ -529,10 +529,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 */ Loading Loading
drivers/hid/hid-input.c +135 −62 Original line number Diff line number Diff line Loading @@ -303,6 +303,7 @@ 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 */ #define HID_BATTERY_QUIRK_IGNORE (1 << 2) /* completely ignore the battery */ static const struct hid_device_id hid_battery_quirks[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, Loading @@ -320,6 +321,9 @@ static const struct hid_device_id hid_battery_quirks[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084), HID_BATTERY_QUIRK_IGNORE }, {} }; Loading @@ -335,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: Loading @@ -350,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: Loading @@ -380,6 +402,21 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_STATUS: 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; Loading @@ -395,27 +432,33 @@ 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) return 0; /* already initialized? */ if (dev->battery != NULL) goto out; /* already initialized? */ 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); if (quirks & HID_BATTERY_QUIRK_IGNORE) 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; Loading @@ -424,11 +467,6 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, psy_desc->use_for_apm = 0; psy_desc->get_property = hidinput_get_battery_property; 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; Loading @@ -447,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)); error = PTR_ERR(dev->battery); hid_warn(dev, "can't register power supply: %d\n", error); goto err_free_name; } 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; } else { power_supply_powers(dev->battery, &dev->dev); } out: return true; return error; } static void hidinput_cleanup_battery(struct hid_device *dev) Loading @@ -473,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, 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, Loading Loading @@ -684,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; Loading Loading @@ -924,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; case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ set_bit(EV_REP, input->evbit); Loading Loading @@ -1006,7 +1071,6 @@ mapped: if (usage->code > max) goto ignore; if (usage->type == EV_ABS) { int a = field->logical_minimum; Loading Loading @@ -1065,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) Loading Loading @@ -1349,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]; Loading @@ -1359,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); } } } Loading
include/linux/hid.h +2 −0 Original line number Diff line number Diff line Loading @@ -529,10 +529,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 */ Loading