RE: [PATCH] platform/x86:dell-laptop:Add battery charging thresholds and charging mode switch.

From: Yuan, Perry
Date: Tue Aug 04 2020 - 02:21:33 EST


> From: Andy Shevchenko <andy.shevchenko@xxxxxxxxx>
> Sent: Wednesday, July 29, 2020 3:32 PM
> To: Yuan, Perry
> Cc: Sebastian Reichel; Matthew Garrett; Pali Rohár; Darren Hart; Andy
> Shevchenko; Limonciello, Mario; Linux PM; Linux Kernel Mailing List; Platform
> Driver
> Subject: Re: [PATCH] platform/x86:dell-laptop:Add battery charging thresholds
> and charging mode switch.
>
>
> [EXTERNAL EMAIL]
>
> On Wed, Jul 29, 2020 at 9:54 AM Perry Yuan <Perry.Yuan@xxxxxxxx> wrote:
> >
> > From: perry_yuan <perry_yuan@xxxxxxxx>
>
> Fix your name, please. Or do you have it spelled in the official documents like
> above?
>
> > The patch control battery charging thresholds when system is under
> > custom charging mode through smbios API and driver`s sys attributes.It
> > also set the percentage bounds for custom charge.
> > Start value must lie in the range [50, 95],End value must lie in the
> > range [55, 100],END must be at least (START + 5).
> >
> > The patch also add the battery charging modes switch support.User can
> > switch the battery charging mode through the new sysfs entry.
>
> The commit message needs rewording. I would recommend reading [1] and [2].
>
> [1]: https://www.kernel.org/doc/html/latest/process/submitting-patches.html
> (section 2)
> [2]: https://chris.beams.io/posts/git-commit/
>
> > Primary battery charging modes valid choices are:
> > ['primarily_ac', 'adaptive', 'custom', 'standard', 'express']
>
> This and documentation are different (at least in terms of case).
>
> Looks like this is something that should be agreed upon on the format and gets
> supported by Power Supply framework in the first place,
>
> > Documentation/ABI/testing/sysfs-class-power | 23 ++
>
> In any case it should be a separate patch. It has nothing to do with Dell, on
> contrary it's a certain device which relies on generic attributes.
>
> ...
>
> > #include <linux/debugfs.h>
> > #include <linux/seq_file.h>
>
> > #include <acpi/video.h>
> > +#include <acpi/battery.h>
>
> Keep it ordered.
>
> > +#include <linux/string.h>
>
> And this is a generic one, should be in generic headers block.
>
> > #include "dell-rbtn.h"
> > #include "dell-smbios.h"
>
> ...
>
> > +static int dell_battery_get(int *start, int *end) {
> > + struct calling_interface_buffer buffer;
> > + struct calling_interface_token *token;
> > + int ret;
> > +
>
> > + if (start) {
> > + token =
> dell_smbios_find_token(BATTERY_CUSTOM_CHARGE_START);
> > + if (!token)
> > + return -ENODEV;
> > + dell_fill_request(&buffer, token->location, 0, 0, 0);
> > + ret = dell_send_request(&buffer,
> > + CLASS_TOKEN_READ, SELECT_TOKEN_STD);
> > + *start = buffer.output[1];
> > + }
>
> (1)
>
> > + if (end) {
> > + token = dell_smbios_find_token(BATTERY_CUSTOM_CHARGE_END);
> > + if (!token)
> > + return -ENODEV;
> > + dell_fill_request(&buffer, token->location, 0, 0, 0);
> > + ret = dell_send_request(&buffer,
> > + CLASS_TOKEN_READ, SELECT_TOKEN_STD);
> > + if (ret)
> > + return -EIO;
> > + *end = buffer.output[1];
> > + }
>
> (2)
>
> (1) and (2) look like twins. Care to provide a helper to simplify?
>
> > + return 0;
> > +}
>
> ...
>
> > + ret = dell_send_request(&buffer,
> > + CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
> > + if (ret)
>
> > + return -EIO;
>
> Why shadowing error code?
>
> ...
>
> > + ret = dell_send_request(&buffer,
> > + CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
> > + if (ret)
> > + return -EIO;
>
> Ditto.
>
> > + return ret;
>
> And here perhaps simple 'return dell_send_request();'?
>
> ...
>
> > + switch (mode) {
> > + case BAT_STANDARD_MODE:
> > + token =
> > + dell_smbios_find_token(BAT_STANDARD_MODE_TOKEN);
>
> > + if (!token)
> > + return -ENODEV;
>
> > + break;
> > + case BAT_EXPRESS_MODE:
> > + token =
> > + dell_smbios_find_token(BAT_EXPRESS_MODE_TOKEN);
>
> > + if (!token)
> > + return -ENODEV;
>
> > + break;
> > + case BAT_PRIMARILY_AC_MODE:
> > + token =
> > + dell_smbios_find_token(BAT_PRIMARILY_AC_MODE_TOKEN);
>
> > + if (!token)
> > + return -ENODEV;
>
> > + break;
> > + case BAT_CUSTOM_MODE:
> > + token = dell_smbios_find_token(BAT_CUSTOM_MODE_TOKEN);
>
> > + if (!token)
> > + return -ENODEV;
>
> > + break;
> > + case BAT_ADAPTIVE_MODE:
> > + token =
> > + dell_smbios_find_token(BAT_ADAPTIVE_MODE_TOKEN);
>
> > + if (!token)
> > + return -ENODEV;
>
> Five times the same lines. Please deduplicate them. One is enough.
>
> > + break;
> > + default:
>
> > + pr_warn("unspported charging mode!\n");
>
> When you have a device use dev_*() to print messages.
>
> > + return -EINVAL;
> > + }
>
> ...
>
> > + ret = dell_send_request(&buffer, CLASS_TOKEN_WRITE,
> SELECT_TOKEN_STD);
> > + if (ret)
> > + return -EIO;
> > +
> > + return ret;
>
> return dell_send_request(...);
>
> ...
>
> > + ret = dell_send_request(&buffer, CLASS_TOKEN_READ,
> SELECT_TOKEN_STD);
> > + if (ret)
> > + return -EIO;
>
> Do not shadow error codes.
>
> > + if (ret == 0)
>
> What is the point?
>
> > + *mode = buffer.output[1];
>
> > + return ret;
>
> Any difference to return 0; ?
>
> ...
>
> > +static ssize_t charge_control_charging_mode_show(struct device *dev,
> > + struct device_attribute *attr,
> > + char *buf)
> > +{
> > + enum battery_charging_mode mode;
> > + char *s = buf;
> > +
> > + for (mode = BAT_STANDARD_MODE; mode < BAT_MAX_MODE; mode++)
> {
> > + if (battery_state[mode]) {
> > + if (mode == bat_chg_current)
> > + s += sprintf(s, "[%s] ", battery_state[mode]);
> > + else
> > + s += sprintf(s, "%s ",
> > + battery_state[mode]);
>
> You have to control buffer boundaries.
>
> > + }
> > + }
>
> > + if (s != buf)
>
> if (s == buf)
> return 0;
>
> > + /* convert the last space to a newline */
> > + *(s-1) = '\n';
> > + return (s - buf);
> > +}
>
> ...
>
> > +static ssize_t charge_control_charging_mode_store(struct device *dev,
> > + struct device_attribute *attr,
> > + const char *buf, size_t size) {
> > + int err;
> > + enum battery_charging_mode mode;
> > + char *p;
> > + int len;
> > + const char *label;
>
> > + p = memchr(buf, '\n', size);
> > + len = p ? p - buf : size;
> > +
> > + for (mode = BAT_STANDARD_MODE; mode < BAT_MAX_MODE; mode++)
> {
> > + label = battery_state[mode];
> > + if (label && len == strlen(label) &&
> > + !strncmp(buf, label, len)) {
> > + bat_chg_current = mode;
> > + break;
> > + }
> > + }
>
> sysfs_match_string()
>
> > + if (mode > BAT_NONE_MODE && mode < BAT_MAX_MODE)
> > + err = battery_charging_mode_set(mode);
> > + else
> > + err = -EINVAL;
> > +
> > + return err ? err : size;
> > +}
>
> ...
>
> > +static ssize_t charge_control_thresholds_store(struct device *dev,
> > + struct device_attribute *attr,
> > + const char *buf, size_t size) {
> > + int err, start, end;
>
> > + if (sscanf(buf, "%d %d", &start, &end) != 2)
> > + return -EINVAL;
>
> It's bad. The rule of thumb is one node per one property.
> On top of that sscanf() is not good, it doesn't check for overflow, use one of
> kstrto*().
>
> > + err = dell_battery_set(start, end);
> > + if (err)
> > + return err;
> > +
> > + return size;
> > +}
>
> ...
>
> > +static int dell_battery_add(struct power_supply *battery) {
> > + device_create_file(&battery->dev,
> > + &dev_attr_charge_control_start_threshold);
> > + device_create_file(&battery->dev,
> > + &dev_attr_charge_control_end_threshold);
> > + device_create_file(&battery->dev,
> > + &dev_attr_charge_control_charging_mode);
>
> Can it be an attribute group?
>
> > +}
>
> ...
>
> > +static struct acpi_battery_hook dell_battery_hook = {
> > + .add_battery = dell_battery_add,
> > + .remove_battery = dell_battery_remove,
> > + .name = "Dell Battery Extension"
>
> Missed comma.
>
> > +};
>
> ...
>
> > +static void dell_battery_setup(struct device *dev) {
> > + enum battery_charging_mode current_mode = BAT_NONE_MODE;
> > +
> > + battery_charging_mode_get(&current_mode);
> > + if (current_mode) {
> > + bat_chg_current = current_mode;
>
> > + pr_debug("battery is present\n");
> > + } else {
> > + pr_debug("battery is not present\n");
> > + }
>
> Why not dev_dbg()? Why do we have these messages at all?
>
> > + battery_hook_register(&dell_battery_hook);
>
> > + device_create_file(dev, &dev_attr_charge_control_thresholds);
>
> Hmm...
>
> > +}
> > +
> > +static void dell_battery_exit(struct device *dev) {
> > + if (bat_chg_current != BAT_NONE_MODE) {
> > + battery_hook_unregister(&dell_battery_hook);
> > + device_remove_file(dev, &dev_attr_charge_control_thresholds);
> > + }
> > +}
>
> ...
>
> > +/*Battery Charging Modes */
>
> Indentation issues.
> Same for many other comments in this change.
>
>
> --
> With Best Regards,
> Andy Shevchenko

Andy .
Thanks a lot for your review, I am working on the V2 to drive your feedback.