GCC Code Coverage Report


Directory: ./
File: src/ppd-driver-platform-profile.c
Date: 2024-09-13 00:56:02
Exec Total Coverage
Lines: 154 163 94.5%
Functions: 18 18 100.0%
Branches: 75 91 82.4%

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