Skip to content

Commit 058de16

Browse files
Wer-Wolfij-intel
authored andcommitted
platform/x86: dell-ddv: Implement the battery matching algorithm
Since commit db0a507 ("ACPICA: Update integer-to-hex-string conversions") the battery serial number is no longer distorted, allowing us to finally implement the battery matching algorithm. The battery matchign algorithm is necessary when translating between ACPI batteries and the associated indices used by the WMI interface based on the battery serial number. Since this serial number can only be retrieved when the battery is present we cannot perform the initial translation inside dell_wmi_ddv_add_battery() because the ACPI battery might be absent at this point in time. Introduce dell_wmi_ddv_battery_translate() which implements the battery matching algorithm and replaces dell_wmi_ddv_battery_index(). Also implement a translation cache for caching previous translations between ACPI batteries and indices. This is necessary because performing a translation can be very expensive. Tested on a Dell Inspiron 3505. Signed-off-by: Armin Wolf <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Ilpo Järvinen <[email protected]> Signed-off-by: Ilpo Järvinen <[email protected]>
1 parent f4856c2 commit 058de16

File tree

2 files changed

+88
-21
lines changed

2 files changed

+88
-21
lines changed

Documentation/wmi/devices/dell-wmi-ddv.rst

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -260,14 +260,6 @@ Some machines like the Dell Inspiron 3505 only support a single battery and thus
260260
ignore the battery index. Because of this the driver depends on the ACPI battery
261261
hook mechanism to discover batteries.
262262

263-
.. note::
264-
The ACPI battery matching algorithm currently used inside the driver is
265-
outdated and does not match the algorithm described above. The reasons for
266-
this are differences in the handling of the ToHexString() ACPI opcode between
267-
Linux and Windows, which distorts the serial number of ACPI batteries on many
268-
machines. Until this issue is resolved, the driver cannot use the above
269-
algorithm.
270-
271263
Reverse-Engineering the DDV WMI interface
272264
=========================================
273265

drivers/platform/x86/dell/dell-wmi-ddv.c

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
#define DELL_DDV_SUPPORTED_VERSION_MAX 3
4040
#define DELL_DDV_GUID "8A42EA14-4F2A-FD45-6422-0087F7A7E608"
4141

42+
/* Battery indices 1, 2 and 3 */
43+
#define DELL_DDV_NUM_BATTERIES 3
44+
4245
#define DELL_EPPID_LENGTH 20
4346
#define DELL_EPPID_EXT_LENGTH 23
4447

@@ -105,6 +108,8 @@ struct dell_wmi_ddv_sensors {
105108
struct dell_wmi_ddv_data {
106109
struct acpi_battery_hook hook;
107110
struct device_attribute eppid_attr;
111+
struct mutex translation_cache_lock; /* Protects the translation cache */
112+
struct power_supply *translation_cache[DELL_DDV_NUM_BATTERIES];
108113
struct dell_wmi_ddv_sensors fans;
109114
struct dell_wmi_ddv_sensors temps;
110115
struct wmi_device *wdev;
@@ -639,15 +644,78 @@ static int dell_wmi_ddv_hwmon_add(struct dell_wmi_ddv_data *data)
639644
return ret;
640645
}
641646

642-
static int dell_wmi_ddv_battery_index(struct acpi_device *acpi_dev, u32 *index)
647+
static int dell_wmi_ddv_battery_translate(struct dell_wmi_ddv_data *data,
648+
struct power_supply *battery, u32 *index)
643649
{
644-
const char *uid_str;
650+
u32 serial_dec, serial_hex, serial;
651+
union power_supply_propval val;
652+
int ret;
653+
654+
guard(mutex)(&data->translation_cache_lock);
655+
656+
for (int i = 0; i < ARRAY_SIZE(data->translation_cache); i++) {
657+
if (data->translation_cache[i] == battery) {
658+
dev_dbg(&data->wdev->dev, "Translation cache hit for battery index %u\n",
659+
i + 1);
660+
*index = i + 1;
661+
return 0;
662+
}
663+
}
664+
665+
dev_dbg(&data->wdev->dev, "Translation cache miss\n");
666+
667+
/* Perform a translation between a ACPI battery and a battery index */
668+
669+
ret = power_supply_get_property(battery, POWER_SUPPLY_PROP_SERIAL_NUMBER, &val);
670+
if (ret < 0)
671+
return ret;
672+
673+
/*
674+
* Some devices display the serial number of the ACPI battery (string!) as a decimal
675+
* number while other devices display it as a hexadecimal number. Because of this we
676+
* have to check both cases.
677+
*/
678+
ret = kstrtou32(val.strval, 16, &serial_hex);
679+
if (ret < 0)
680+
return ret; /* Should never fail */
681+
682+
ret = kstrtou32(val.strval, 10, &serial_dec);
683+
if (ret < 0)
684+
serial_dec = 0; /* Can fail, thus we only mark serial_dec as invalid */
685+
686+
for (int i = 0; i < ARRAY_SIZE(data->translation_cache); i++) {
687+
ret = dell_wmi_ddv_query_integer(data->wdev, DELL_DDV_BATTERY_SERIAL_NUMBER, i + 1,
688+
&serial);
689+
if (ret < 0)
690+
return ret;
645691

646-
uid_str = acpi_device_uid(acpi_dev);
647-
if (!uid_str)
648-
return -ENODEV;
692+
/* A serial number of 0 signals that this index is not associated with a battery */
693+
if (!serial)
694+
continue;
649695

650-
return kstrtou32(uid_str, 10, index);
696+
if (serial == serial_dec || serial == serial_hex) {
697+
dev_dbg(&data->wdev->dev, "Translation cache update for battery index %u\n",
698+
i + 1);
699+
data->translation_cache[i] = battery;
700+
*index = i + 1;
701+
return 0;
702+
}
703+
}
704+
705+
return -ENODEV;
706+
}
707+
708+
static void dell_wmi_battery_invalidate(struct dell_wmi_ddv_data *data,
709+
struct power_supply *battery)
710+
{
711+
guard(mutex)(&data->translation_cache_lock);
712+
713+
for (int i = 0; i < ARRAY_SIZE(data->translation_cache); i++) {
714+
if (data->translation_cache[i] == battery) {
715+
data->translation_cache[i] = NULL;
716+
return;
717+
}
718+
}
651719
}
652720

653721
static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, char *buf)
@@ -657,7 +725,7 @@ static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, cha
657725
u32 index;
658726
int ret;
659727

660-
ret = dell_wmi_ddv_battery_index(to_acpi_device(dev->parent), &index);
728+
ret = dell_wmi_ddv_battery_translate(data, to_power_supply(dev), &index);
661729
if (ret < 0)
662730
return ret;
663731

@@ -684,7 +752,7 @@ static int dell_wmi_ddv_get_property(struct power_supply *psy, const struct powe
684752
u32 index, value;
685753
int ret;
686754

687-
ret = dell_wmi_ddv_battery_index(to_acpi_device(psy->dev.parent), &index);
755+
ret = dell_wmi_ddv_battery_translate(data, psy, &index);
688756
if (ret < 0)
689757
return ret;
690758

@@ -719,13 +787,12 @@ static const struct power_supply_ext dell_wmi_ddv_extension = {
719787
static int dell_wmi_ddv_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
720788
{
721789
struct dell_wmi_ddv_data *data = container_of(hook, struct dell_wmi_ddv_data, hook);
722-
u32 index;
723790
int ret;
724791

725-
/* Return 0 instead of error to avoid being unloaded */
726-
ret = dell_wmi_ddv_battery_index(to_acpi_device(battery->dev.parent), &index);
727-
if (ret < 0)
728-
return 0;
792+
/*
793+
* We cannot do the battery matching here since the battery might be absent, preventing
794+
* us from reading the serial number.
795+
*/
729796

730797
ret = device_create_file(&battery->dev, &data->eppid_attr);
731798
if (ret < 0)
@@ -749,11 +816,19 @@ static int dell_wmi_ddv_remove_battery(struct power_supply *battery, struct acpi
749816
device_remove_file(&battery->dev, &data->eppid_attr);
750817
power_supply_unregister_extension(battery, &dell_wmi_ddv_extension);
751818

819+
dell_wmi_battery_invalidate(data, battery);
820+
752821
return 0;
753822
}
754823

755824
static int dell_wmi_ddv_battery_add(struct dell_wmi_ddv_data *data)
756825
{
826+
int ret;
827+
828+
ret = devm_mutex_init(&data->wdev->dev, &data->translation_cache_lock);
829+
if (ret < 0)
830+
return ret;
831+
757832
data->hook.name = "Dell DDV Battery Extension";
758833
data->hook.add_battery = dell_wmi_ddv_add_battery;
759834
data->hook.remove_battery = dell_wmi_ddv_remove_battery;

0 commit comments

Comments
 (0)