Commit 581c4484 authored by Dmitry Torokhov's avatar Dmitry Torokhov Committed by Jiri Kosina

HID: input: map digitizer battery usage

We already mapped battery strength reports from the generic device
control page, but we did not update capacity from input reports, nor we
mapped the battery strength report from the digitizer page, so let's
implement this now.

Batteries driven by the input reports will now start in "unknown" state,
and will get updated once we receive first report containing battery
strength from the device.
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent a9d0683e
...@@ -340,13 +340,45 @@ static unsigned find_battery_quirk(struct hid_device *hdev) ...@@ -340,13 +340,45 @@ static unsigned find_battery_quirk(struct hid_device *hdev)
return quirks; 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, static int hidinput_get_battery_property(struct power_supply *psy,
enum power_supply_property prop, enum power_supply_property prop,
union power_supply_propval *val) union power_supply_propval *val)
{ {
struct hid_device *dev = power_supply_get_drvdata(psy); struct hid_device *dev = power_supply_get_drvdata(psy);
int value;
int ret = 0; int ret = 0;
__u8 *buf;
switch (prop) { switch (prop) {
case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_PRESENT:
...@@ -355,29 +387,15 @@ static int hidinput_get_battery_property(struct power_supply *psy, ...@@ -355,29 +387,15 @@ static int hidinput_get_battery_property(struct power_supply *psy,
break; break;
case POWER_SUPPLY_PROP_CAPACITY: case POWER_SUPPLY_PROP_CAPACITY:
if (dev->battery_report_type == HID_FEATURE_REPORT) {
buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL); value = hidinput_query_battery_capacity(dev);
if (!buf) { if (value < 0)
ret = -ENOMEM; return value;
break; } else {
} value = dev->battery_capacity;
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;
} }
ret = 0;
if (dev->battery_min < dev->battery_max && val->intval = value;
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);
break; break;
case POWER_SUPPLY_PROP_MODEL_NAME: case POWER_SUPPLY_PROP_MODEL_NAME:
...@@ -385,7 +403,22 @@ static int hidinput_get_battery_property(struct power_supply *psy, ...@@ -385,7 +403,22 @@ static int hidinput_get_battery_property(struct power_supply *psy,
break; break;
case POWER_SUPPLY_PROP_STATUS: 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; break;
case POWER_SUPPLY_PROP_SCOPE: case POWER_SUPPLY_PROP_SCOPE:
...@@ -400,18 +433,16 @@ static int hidinput_get_battery_property(struct power_supply *psy, ...@@ -400,18 +433,16 @@ static int hidinput_get_battery_property(struct power_supply *psy,
return ret; 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, }; struct power_supply_config psy_cfg = { .drv_data = dev, };
unsigned quirks; unsigned quirks;
s32 min, max; s32 min, max;
int error;
if (field->usage->hid != HID_DC_BATTERYSTRENGTH) if (dev->battery)
return false; /* no match */ return 0; /* already initialized? */
if (dev->battery != NULL)
goto out; /* already initialized? */
quirks = find_battery_quirk(dev); quirks = find_battery_quirk(dev);
...@@ -419,16 +450,16 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, ...@@ -419,16 +450,16 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
dev->bus, dev->vendor, dev->product, dev->version, quirks); dev->bus, dev->vendor, dev->product, dev->version, quirks);
if (quirks & HID_BATTERY_QUIRK_IGNORE) if (quirks & HID_BATTERY_QUIRK_IGNORE)
goto out; return 0;
psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL); psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL);
if (psy_desc == NULL) if (!psy_desc)
goto out; return -ENOMEM;
psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq); psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq);
if (psy_desc->name == NULL) { if (!psy_desc->name) {
kfree(psy_desc); error = -ENOMEM;
goto out; goto err_free_mem;
} }
psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
...@@ -455,17 +486,20 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, ...@@ -455,17 +486,20 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg); dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
if (IS_ERR(dev->battery)) { if (IS_ERR(dev->battery)) {
hid_warn(dev, "can't register power supply: %ld\n", error = PTR_ERR(dev->battery);
PTR_ERR(dev->battery)); hid_warn(dev, "can't register power supply: %d\n", error);
kfree(psy_desc->name); goto err_free_name;
kfree(psy_desc);
dev->battery = NULL;
} else {
power_supply_powers(dev->battery, &dev->dev);
} }
out: power_supply_powers(dev->battery, &dev->dev);
return true; 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) static void hidinput_cleanup_battery(struct hid_device *dev)
...@@ -481,16 +515,33 @@ static void hidinput_cleanup_battery(struct hid_device *dev) ...@@ -481,16 +515,33 @@ static void hidinput_cleanup_battery(struct hid_device *dev)
kfree(psy_desc); kfree(psy_desc);
dev->battery = NULL; 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 */ #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) struct hid_field *field)
{ {
return false; return 0;
} }
static void hidinput_cleanup_battery(struct hid_device *dev) 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 */ #endif /* CONFIG_HID_BATTERY_STRENGTH */
static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
...@@ -710,6 +761,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -710,6 +761,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
} }
break; break;
case 0x3b: /* Battery Strength */
hidinput_setup_battery(device, HID_INPUT_REPORT, field);
usage->type = EV_PWR;
goto ignore;
case 0x3c: /* Invert */ case 0x3c: /* Invert */
map_key_clear(BTN_TOOL_RUBBER); map_key_clear(BTN_TOOL_RUBBER);
break; break;
...@@ -944,11 +1000,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -944,11 +1000,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
break; break;
case HID_UP_GENDEVCTRLS: 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; goto ignore;
else }
goto unknown; goto unknown;
break;
case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */
set_bit(EV_REP, input->evbit); set_bit(EV_REP, input->evbit);
...@@ -1031,7 +1089,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -1031,7 +1089,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
if (usage->code > max) if (usage->code > max)
goto ignore; goto ignore;
if (usage->type == EV_ABS) { if (usage->type == EV_ABS) {
int a = field->logical_minimum; int a = field->logical_minimum;
...@@ -1090,14 +1147,19 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct ...@@ -1090,14 +1147,19 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
struct input_dev *input; struct input_dev *input;
unsigned *quirks = &hid->quirks; unsigned *quirks = &hid->quirks;
if (!field->hidinput) if (!usage->type)
return; return;
input = field->hidinput->input; if (usage->type == EV_PWR) {
hidinput_update_battery(hid, value);
return;
}
if (!usage->type) if (!field->hidinput)
return; return;
input = field->hidinput->input;
if (usage->hat_min < usage->hat_max || usage->hat_dir) { if (usage->hat_min < usage->hat_max || usage->hat_dir) {
int hat_dir = usage->hat_dir; int hat_dir = usage->hat_dir;
if (!hat_dir) if (!hat_dir)
...@@ -1373,6 +1435,7 @@ static void report_features(struct hid_device *hid) ...@@ -1373,6 +1435,7 @@ static void report_features(struct hid_device *hid)
struct hid_driver *drv = hid->driver; struct hid_driver *drv = hid->driver;
struct hid_report_enum *rep_enum; struct hid_report_enum *rep_enum;
struct hid_report *rep; struct hid_report *rep;
struct hid_usage *usage;
int i, j; int i, j;
rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
...@@ -1383,12 +1446,15 @@ static void report_features(struct hid_device *hid) ...@@ -1383,12 +1446,15 @@ static void report_features(struct hid_device *hid)
continue; continue;
for (j = 0; j < rep->field[i]->maxusage; j++) { for (j = 0; j < rep->field[i]->maxusage; j++) {
usage = &rep->field[i]->usage[j];
/* Verify if Battery Strength feature is available */ /* 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) if (drv->feature_mapping)
drv->feature_mapping(hid, rep->field[i], drv->feature_mapping(hid, rep->field[i], usage);
rep->field[i]->usage + j);
} }
} }
} }
......
...@@ -542,10 +542,12 @@ struct hid_device { /* device report descriptor */ ...@@ -542,10 +542,12 @@ struct hid_device { /* device report descriptor */
* battery is non-NULL. * battery is non-NULL.
*/ */
struct power_supply *battery; struct power_supply *battery;
__s32 battery_capacity;
__s32 battery_min; __s32 battery_min;
__s32 battery_max; __s32 battery_max;
__s32 battery_report_type; __s32 battery_report_type;
__s32 battery_report_id; __s32 battery_report_id;
bool battery_reported;
#endif #endif
unsigned int status; /* see STAT flags above */ unsigned int status; /* see STAT flags above */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment