GCC Code Coverage Report


Directory: ./
File: src/ppd-driver-platform-profile.c
Date: 2026-01-04 01:15:03
Exec Total Coverage
Lines: 155 166 93.4%
Functions: 18 18 100.0%
Branches: 86 105 81.9%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2020 Bastien Nocera <hadess@hadess.net>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3 as published by
6 * the Free Software Foundation.
7 *
8 */
9
10 #define G_LOG_DOMAIN "PlatformDriver"
11
12 #include <gudev/gudev.h>
13 #include <gio/gio.h>
14
15 #include "ppd-driver-platform-profile.h"
16 #include "ppd-utils.h"
17
18 #define LAPMODE_SYSFS_NAME "dytc_lapmode"
19 #define ACPI_PLATFORM_PROFILE_PATH "/sys/firmware/acpi/platform_profile"
20 #define ACPI_PLATFORM_PROFILE_CHOICES_PATH "/sys/firmware/acpi/platform_profile_choices"
21
22 struct _PpdDriverPlatformProfile
23 {
24 PpdDriver parent_instance;
25
26 PpdProbeResult probe_result;
27 GUdevDevice *device;
28 int lapmode;
29 PpdProfile acpi_platform_profile;
30 char **profile_choices;
31 gboolean has_low_power;
32 GFileMonitor *lapmode_mon;
33 GFileMonitor *acpi_platform_profile_mon;
34 gulong acpi_platform_profile_changed_id;
35 };
36
37
4/5
✓ Branch 0 taken 142 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 142 times.
✓ Branch 3 taken 142 times.
✗ Branch 4 not taken.
884 G_DEFINE_TYPE (PpdDriverPlatformProfile, ppd_driver_platform_profile, PPD_TYPE_DRIVER_PLATFORM)
38
39 static GObject*
40 158 ppd_driver_platform_profile_constructor (GType type,
41 guint n_construct_params,
42 GObjectConstructParam *construct_params)
43 {
44 158 GObject *object;
45
46 158 object = G_OBJECT_CLASS (ppd_driver_platform_profile_parent_class)->constructor (type,
47 n_construct_params,
48 construct_params);
49 158 g_object_set (object,
50 "driver-name", "platform_profile",
51 "profiles", PPD_PROFILE_PERFORMANCE | PPD_PROFILE_BALANCED | PPD_PROFILE_POWER_SAVER,
52 NULL);
53
54 158 return object;
55 }
56
57 static const char *
58 294 profile_to_acpi_platform_profile_value (PpdDriverPlatformProfile *self,
59 PpdProfile profile)
60 {
61
3/4
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 78 times.
294 switch (profile) {
62 66 case PPD_PROFILE_POWER_SAVER:
63
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
66 if (!self->has_low_power)
64 return "balanced";
65
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 58 times.
66 if (g_strv_contains ((const char * const*) self->profile_choices, "low-power"))
66 return "low-power";
67
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
8 if (g_strv_contains ((const char * const*) self->profile_choices, "cool"))
68 4 return "cool";
69 return "quiet";
70 case PPD_PROFILE_BALANCED:
71 return "balanced";
72 case PPD_PROFILE_PERFORMANCE:
73 return "performance";
74 }
75
76 g_debug ("Unhandled ACPI platform profile '%d'", profile);
77 g_return_val_if_reached (NULL);
78 }
79
80 static PpdProfile
81 345 acpi_platform_profile_value_to_profile (const char *str)
82 {
83
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 345 times.
345 if (str == NULL)
84 return PPD_PROFILE_UNSET;
85
86
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 343 times.
345 if (g_str_equal (str, "custom"))
87 return PPD_PROFILE_UNSET;
88
89
2/2
✓ Branch 0 taken 284 times.
✓ Branch 1 taken 59 times.
343 if (g_str_equal (str, "low-power") ||
90
2/2
✓ Branch 0 taken 278 times.
✓ Branch 1 taken 6 times.
284 g_str_equal (str, "quiet") ||
91
2/2
✓ Branch 0 taken 272 times.
✓ Branch 1 taken 6 times.
278 g_str_equal (str, "cool"))
92 return PPD_PROFILE_POWER_SAVER;
93
94
2/2
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 149 times.
272 if (g_str_equal (str, "balanced") ||
95
1/2
✓ Branch 0 taken 123 times.
✗ Branch 1 not taken.
123 g_str_equal (str, "balanced_performance"))
96 return PPD_PROFILE_BALANCED;
97
98
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 123 times.
123 if (g_str_equal (str, "performance"))
99 return PPD_PROFILE_PERFORMANCE;
100
101 g_debug ("Unhandled ACPI platform profile '%s'", str);
102 g_return_val_if_reached (PPD_PROFILE_UNSET);
103 }
104
105 static PpdProfile
106 198 read_platform_profile (void)
107 {
108 396 g_autofree char *platform_profile_path = NULL;
109 198 g_autofree char *new_profile_str = NULL;
110 198 g_autoptr(GError) error = NULL;
111 198 PpdProfile new_profile;
112
113 198 platform_profile_path = ppd_utils_get_sysfs_path (ACPI_PLATFORM_PROFILE_PATH);
114
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 198 times.
198 if (!g_file_get_contents (platform_profile_path,
115 &new_profile_str, NULL, &error)) {
116 g_debug ("Failed to get contents for '%s': %s",
117 platform_profile_path,
118 error->message);
119 return PPD_PROFILE_UNSET;
120 }
121
122 198 new_profile = acpi_platform_profile_value_to_profile (g_strchomp (new_profile_str));
123 198 g_debug ("ACPI performance_profile is now '%s', so profile is detected as %s",
124 g_strchomp (new_profile_str),
125 ppd_profile_to_str (new_profile));
126 198 return new_profile;
127 }
128
129 static gboolean
130 55 save_platform_profile_choices (PpdDriverPlatformProfile *self)
131 {
132 110 g_autofree char *platform_profile_choices_path = NULL;
133 55 g_autofree char *choices_str = NULL;
134 55 g_autoptr(GError) error = NULL;
135
136 55 platform_profile_choices_path = ppd_utils_get_sysfs_path (ACPI_PLATFORM_PROFILE_CHOICES_PATH);
137
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 55 times.
55 if (!g_file_get_contents (platform_profile_choices_path,
138 &choices_str, NULL, &error)) {
139 g_debug ("Failed to get contents for '%s': %s",
140 platform_profile_choices_path,
141 error->message);
142 return FALSE;
143 }
144
145 55 self->profile_choices = g_strsplit_set (choices_str, " \n", -1);
146 55 return TRUE;
147 }
148
149 static PpdProbeResult
150 55 verify_acpi_platform_profile_choices (PpdDriverPlatformProfile *self)
151 {
152 55 const char * const *choices = (const char * const*) self->profile_choices;
153
154
3/4
✓ Branch 1 taken 51 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 51 times.
106 if (g_strv_contains (choices, "balanced") &&
155 51 g_strv_contains (choices, "performance")) {
156
4/4
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 47 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
55 if (g_strv_contains (choices, "low-power") ||
157
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
6 g_strv_contains (choices, "quiet") ||
158 2 g_strv_contains (choices, "cool"))
159 51 self->has_low_power = TRUE;
160 else
161 g_debug ("No \"low-power\" profile for device, will be emulated");
162 51 return PPD_PROBE_RESULT_SUCCESS;
163 }
164 return PPD_PROBE_RESULT_DEFER;
165 }
166
167 static void
168 16 update_dytc_lapmode_state (PpdDriverPlatformProfile *self)
169 {
170 16 int new_lapmode;
171
172 16 new_lapmode = g_udev_device_get_sysfs_attr_as_int_uncached (self->device, LAPMODE_SYSFS_NAME);
173
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if (new_lapmode == self->lapmode)
174 return;
175
176 8 self->lapmode = new_lapmode;
177
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 6 times.
12 g_debug ("dytc_lapmode is now %s, so profile is %s",
178 self->lapmode ? "on" : "off",
179 self->lapmode ? "degraded" : "not degraded");
180 8 g_object_set (G_OBJECT (self),
181
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 "performance-degraded", self->lapmode ? "lap-detected" : NULL,
182 NULL);
183 }
184
185 static void
186 198 update_acpi_platform_profile_state (PpdDriverPlatformProfile *self)
187 {
188 198 PpdProfile new_profile;
189
190 198 new_profile = read_platform_profile ();
191
2/2
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 2 times.
198 if (new_profile == PPD_PROFILE_UNSET ||
192
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 145 times.
196 new_profile == self->acpi_platform_profile)
193 return;
194
195 51 self->acpi_platform_profile = new_profile;
196 51 ppd_driver_emit_profile_changed (PPD_DRIVER (self), new_profile);
197 }
198
199 static void
200 22 lapmode_changed (GFileMonitor *monitor,
201 GFile *file,
202 GFile *other_file,
203 GFileMonitorEvent event_type,
204 gpointer user_data)
205 {
206 22 PpdDriverPlatformProfile *self = user_data;
207
208 22 g_debug (LAPMODE_SYSFS_NAME " attribute changed (event: %d)", event_type);
209
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 g_return_if_fail (event_type != G_FILE_MONITOR_EVENT_DELETED);
210
211
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 14 times.
22 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
212 8 update_dytc_lapmode_state (self);
213 }
214
215 static void
216 442 acpi_platform_profile_changed (GFileMonitor *monitor,
217 GFile *file,
218 GFile *other_file,
219 GFileMonitorEvent event_type,
220 gpointer user_data)
221 {
222 442 PpdDriverPlatformProfile *self = user_data;
223
224 442 g_debug (ACPI_PLATFORM_PROFILE_PATH " changed (%d)", event_type);
225
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 438 times.
442 if (self->probe_result == PPD_PROBE_RESULT_DEFER) {
226 4 g_signal_emit_by_name (G_OBJECT (self), "probe-request", 0);
227 4 return;
228 }
229
230
1/2
✓ Branch 0 taken 438 times.
✗ Branch 1 not taken.
438 g_return_if_fail (event_type != G_FILE_MONITOR_EVENT_DELETED);
231
232
2/2
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 291 times.
438 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
233 147 update_acpi_platform_profile_state (self);
234 }
235
236 static gboolean
237 151 ppd_driver_platform_profile_activate_profile (PpdDriver *driver,
238 PpdProfile profile,
239 PpdProfileActivationReason reason,
240 GError **error)
241 {
242 151 PpdDriverPlatformProfile *self = PPD_DRIVER_PLATFORM_PROFILE (driver);
243 302 g_autoptr(GError) local_error = NULL;
244
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 151 times.
151 g_autofree char *platform_profile_path = NULL;
245 151 const char *platform_profile_value;
246
247
1/2
✓ Branch 0 taken 151 times.
✗ Branch 1 not taken.
151 g_return_val_if_fail (self->acpi_platform_profile_mon, FALSE);
248
249
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 147 times.
151 if (self->acpi_platform_profile == profile) {
250 4 g_debug ("Can't switch to %s mode, already there",
251 ppd_profile_to_str (profile));
252 4 return TRUE;
253 }
254
255 147 platform_profile_value = profile_to_acpi_platform_profile_value (self, profile);
256
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 147 times.
147 if (self->acpi_platform_profile == acpi_platform_profile_value_to_profile (platform_profile_value)) {
257 g_debug ("Not switching to platform_profile %s, emulating for %s, already there",
258 platform_profile_value,
259 ppd_profile_to_str (profile));
260 return TRUE;
261 }
262
263 147 g_signal_handler_block (G_OBJECT (self->acpi_platform_profile_mon), self->acpi_platform_profile_changed_id);
264 147 platform_profile_path = ppd_utils_get_sysfs_path (ACPI_PLATFORM_PROFILE_PATH);
265
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 145 times.
147 if (!ppd_utils_write (platform_profile_path,
266 profile_to_acpi_platform_profile_value (self, profile), &local_error)) {
267 2 g_debug ("Failed to write to acpi_platform_profile: %s", local_error->message);
268 2 g_propagate_prefixed_error (error, g_steal_pointer (&local_error),
269 "Failed to write to acpi_platform_profile: ");
270 2 g_signal_handler_unblock (G_OBJECT (self->acpi_platform_profile_mon), self->acpi_platform_profile_changed_id);
271 2 return FALSE;
272 }
273 145 g_signal_handler_unblock (G_OBJECT (self->acpi_platform_profile_mon), self->acpi_platform_profile_changed_id);
274
275 145 g_debug ("Successfully switched to profile %s", ppd_profile_to_str (profile));
276 145 self->acpi_platform_profile = profile;
277 145 return TRUE;
278 }
279
280 static int
281 8 find_dytc (GUdevDevice *dev,
282 gpointer user_data)
283 {
284
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (g_strcmp0 (g_udev_device_get_name (dev), "thinkpad_acpi") != 0)
285 return 1;
286
287
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (!g_udev_device_get_sysfs_attr (dev, LAPMODE_SYSFS_NAME))
288 return 1;
289
290 return 0;
291 }
292
293 static PpdProbeResult
294 152 ppd_driver_platform_profile_probe (PpdDriver *driver)
295 {
296 152 PpdDriverPlatformProfile *self = PPD_DRIVER_PLATFORM_PROFILE (driver);
297 304 g_autoptr(GFile) acpi_platform_profile = NULL;
298
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 97 times.
152 g_autofree char *platform_profile_path = NULL;
299
300
1/2
✓ Branch 0 taken 152 times.
✗ Branch 1 not taken.
152 g_return_val_if_fail (self->probe_result == PPD_PROBE_RESULT_UNSET, PPD_PROBE_RESULT_FAIL);
301
302 /* Profile interface */
303 152 platform_profile_path = ppd_utils_get_sysfs_path (ACPI_PLATFORM_PROFILE_PATH);
304
2/2
✓ Branch 1 taken 97 times.
✓ Branch 2 taken 55 times.
152 if (!g_file_test (platform_profile_path, G_FILE_TEST_EXISTS)) {
305 97 g_debug ("No platform_profile sysfs file");
306 97 return PPD_PROBE_RESULT_FAIL;
307 }
308
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 55 times.
55 if (!save_platform_profile_choices (self))
309 return PPD_PROBE_RESULT_FAIL;
310 55 self->probe_result = verify_acpi_platform_profile_choices (self);
311 55 if (self->probe_result == PPD_PROBE_RESULT_FAIL) {
312 g_debug ("No supported platform_profile choices");
313 return self->probe_result;
314 }
315
316 55 acpi_platform_profile = g_file_new_for_path (platform_profile_path);
317 55 self->acpi_platform_profile_mon = g_file_monitor (acpi_platform_profile,
318 G_FILE_MONITOR_NONE,
319 NULL,
320 NULL);
321 110 self->acpi_platform_profile_changed_id =
322 55 g_signal_connect (G_OBJECT (self->acpi_platform_profile_mon), "changed",
323 G_CALLBACK (acpi_platform_profile_changed), self);
324
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 51 times.
55 if (self->probe_result == PPD_PROBE_RESULT_DEFER) {
325 4 g_debug ("Monitoring platform_profile sysfs file");
326 4 return self->probe_result;
327 }
328
329 /* Lenovo-specific proximity sensor */
330 51 self->device = ppd_utils_find_device ("platform",
331 (GCompareFunc) find_dytc,
332 NULL);
333
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 8 times.
51 if (!self->device)
334 43 goto out;
335
336 8 self->lapmode_mon = ppd_utils_monitor_sysfs_attr (self->device,
337 LAPMODE_SYSFS_NAME,
338 NULL);
339 8 g_signal_connect_object (G_OBJECT (self->lapmode_mon), "changed",
340 G_CALLBACK (lapmode_changed), self, 0);
341 8 update_dytc_lapmode_state (self);
342
343 51 out:
344 51 update_acpi_platform_profile_state (self);
345
346
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 8 times.
51 g_debug ("%s a dytc_lapmode sysfs attribute to thinkpad_acpi",
347 self->device ? "Found" : "Didn't find");
348 51 return PPD_PROBE_RESULT_SUCCESS;
349 }
350
351 static void
352 158 ppd_driver_platform_profile_finalize (GObject *object)
353 {
354 158 PpdDriverPlatformProfile *driver;
355
356 158 driver = PPD_DRIVER_PLATFORM_PROFILE (object);
357
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 103 times.
158 g_clear_signal_handler (&driver->acpi_platform_profile_changed_id,
358 driver->acpi_platform_profile_mon);
359
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 103 times.
158 g_clear_pointer (&driver->profile_choices, g_strfreev);
360
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 150 times.
158 g_clear_object (&driver->device);
361
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 150 times.
158 g_clear_object (&driver->lapmode_mon);
362
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 103 times.
158 g_clear_object (&driver->acpi_platform_profile_mon);
363 158 G_OBJECT_CLASS (ppd_driver_platform_profile_parent_class)->finalize (object);
364 158 }
365
366 static void
367 142 ppd_driver_platform_profile_class_init (PpdDriverPlatformProfileClass *klass)
368 {
369 142 GObjectClass *object_class;
370 142 PpdDriverClass *driver_class;
371
372 142 object_class = G_OBJECT_CLASS (klass);
373 142 object_class->constructor = ppd_driver_platform_profile_constructor;
374 142 object_class->finalize = ppd_driver_platform_profile_finalize;
375
376 142 driver_class = PPD_DRIVER_CLASS (klass);
377 142 driver_class->probe = ppd_driver_platform_profile_probe;
378 142 driver_class->activate_profile = ppd_driver_platform_profile_activate_profile;
379 }
380
381 static void
382 158 ppd_driver_platform_profile_init (PpdDriverPlatformProfile *self)
383 {
384 158 self->probe_result = PPD_PROBE_RESULT_UNSET;
385 158 }
386