GCC Code Coverage Report


Directory: ./
File: src/ppd-driver-platform-profile.c
Date: 2025-03-30 20:28:01
Exec Total Coverage
Lines: 156 164 95.1%
Functions: 18 18 100.0%
Branches: 85 101 84.2%

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