[PATCH v4 05/12] Input: xbox_gip - Add battery support
Vicki Pfau <[email protected]>
| Newsgroups | org.kernel.vger.linux-input |
|---|---|
| Message-ID | <[email protected]> |
Controllers are required to give information about the battery in the controller, if present. This patch exposes that information as a power_supply device. Signed-off-by: Vicki Pfau <[email protected]> --- drivers/input/joystick/gip/gip-core.c | 97 +++++++++++++++++++++++++++ drivers/input/joystick/gip/gip.h | 3 + 2 files changed, 100 insertions(+) diff --git a/drivers/input/joystick/gip/gip-core.c b/drivers/input/joystick/gip/gip-core.c index c833b10259ab..d694ac551639 100644 --- a/drivers/input/joystick/gip/gip-core.c +++ b/drivers/input/joystick/gip/gip-core.c @@ -255,6 +255,13 @@ static const struct gip_audio_format gip_audio_format_table[MAX_GIP_AUDIO_FORMAT }; +static enum power_supply_property gip_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_STATUS, +}; + static const struct gip_quirks base_quirks[] = { { GIP_VID_PDP, GIP_PID_PDP_ROCK_CANDY, 0, .quirks = GIP_QUIRK_NO_HELLO }, @@ -1196,6 +1203,92 @@ static int gip_guide_led_probe(struct gip_attachment *attachment, struct device return rc; } +static int gip_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, union power_supply_propval *val) +{ + struct gip_attachment *attachment = power_supply_get_drvdata(psy); + + guard(mutex)(&attachment->lock); + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = attachment->status.base.battery_type != GIP_BATTERY_ABSENT; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + case POWER_SUPPLY_PROP_STATUS: + if (attachment->status.base.battery_type == GIP_BATTERY_ABSENT) { + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + switch (attachment->status.base.charge) { + case GIP_CHARGING: + if (attachment->status.base.battery_level == GIP_BATTERY_FULL) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case GIP_NOT_CHARGING: + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case GIP_CHARGE_ERROR: + default: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + } + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + if (attachment->status.base.battery_type == GIP_BATTERY_ABSENT) { + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + } else { + switch (attachment->status.base.battery_level) { + case GIP_BATTERY_CRITICAL: + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + break; + case GIP_BATTERY_LOW: + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + break; + case GIP_BATTERY_MEDIUM: + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + break; + case GIP_BATTERY_FULL: + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + break; + default: + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; + break; + } + break; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int gip_battery_create(struct gip_attachment *attachment, struct device *dev) +{ + struct power_supply_config supply_config = { .drv_data = attachment, }; + + attachment->battery_desc.properties = gip_battery_props; + attachment->battery_desc.num_properties = ARRAY_SIZE(gip_battery_props); + attachment->battery_desc.get_property = gip_battery_get_property; + attachment->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; + attachment->battery_desc.name = devm_kasprintf(dev, GFP_KERNEL, + "gip-battery-%s", dev_name(dev)); + + if (!attachment->battery_desc.name) + return -ENOMEM; + + attachment->battery = devm_power_supply_register(dev, + &attachment->battery_desc, &supply_config); + if (IS_ERR(attachment->battery)) + return PTR_ERR(attachment->battery); + + return power_supply_powers(attachment->battery, dev); +} + static bool gip_send_set_device_state(struct gip_attachment *attachment, uint8_t state) { uint8_t buffer[] = { state }; @@ -1308,6 +1401,9 @@ static int gip_setup_input_device(struct gip_attachment *attachment) rc = gip_guide_led_probe(attachment, &input->dev); if (rc) gip_err(attachment, "Failed to register LEDs: %d\n", rc); + rc = gip_battery_create(attachment, &input->dev); + if (rc) + gip_err(attachment, "Failed to register battery: %d\n", rc); return 0; @@ -2623,6 +2719,7 @@ static int gip_shutdown(struct gip_device *device) hdev = rcu_dereference(attachment->hdev); rcu_read_unlock(); + attachment->battery = NULL; rcu_assign_pointer(attachment->input, NULL); rcu_assign_pointer(attachment->hdev, NULL); synchronize_rcu(); diff --git a/drivers/input/joystick/gip/gip.h b/drivers/input/joystick/gip/gip.h index fbcf66e18158..e7953c683c51 100644 --- a/drivers/input/joystick/gip/gip.h +++ b/drivers/input/joystick/gip/gip.h @@ -14,6 +14,7 @@ #include <linux/hid.h> #include <linux/led-class-multicolor.h> +#include <linux/power_supply.h> #include <linux/rcupdate.h> #include <linux/usb/input.h> @@ -241,6 +242,8 @@ struct gip_attachment { struct led_classdev_mc color; } guide_led; + struct power_supply *battery; + struct power_supply_desc battery_desc; struct gip_extended_status status; enum gip_elite_button_format xbe_format; -- 2.54.0