1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * (C) 2004-2009 Dominik Brodowski <[email protected]>
4 */
5
6
7 #include <stdio.h>
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15
16 #include "cpufreq.h"
17 #include "cpupower_intern.h"
18
19 /* CPUFREQ sysfs access **************************************************/
20
21 /* helper function to read file from /sys into given buffer */
22 /* fname is a relative path under "cpuX/cpufreq" dir */
sysfs_cpufreq_read_file(unsigned int cpu,const char * fname,char * buf,size_t buflen)23 static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname,
24 char *buf, size_t buflen)
25 {
26 char path[SYSFS_PATH_MAX];
27
28 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
29 cpu, fname);
30 return cpupower_read_sysfs(path, buf, buflen);
31 }
32
33 /* helper function to write a new value to a /sys file */
34 /* fname is a relative path under "cpuX/cpufreq" dir */
sysfs_cpufreq_write_file(unsigned int cpu,const char * fname,const char * value,size_t len)35 static unsigned int sysfs_cpufreq_write_file(unsigned int cpu,
36 const char *fname,
37 const char *value, size_t len)
38 {
39 char path[SYSFS_PATH_MAX];
40 int fd;
41 ssize_t numwrite;
42
43 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
44 cpu, fname);
45
46 fd = open(path, O_WRONLY);
47 if (fd == -1)
48 return 0;
49
50 numwrite = write(fd, value, len);
51 if (numwrite < 1) {
52 close(fd);
53 return 0;
54 }
55
56 close(fd);
57
58 return (unsigned int) numwrite;
59 }
60
61 /* read access to files which contain one numeric value */
62
63 enum cpufreq_value {
64 CPUINFO_CUR_FREQ,
65 CPUINFO_MIN_FREQ,
66 CPUINFO_MAX_FREQ,
67 CPUINFO_LATENCY,
68 SCALING_CUR_FREQ,
69 SCALING_MIN_FREQ,
70 SCALING_MAX_FREQ,
71 STATS_NUM_TRANSITIONS,
72 MAX_CPUFREQ_VALUE_READ_FILES
73 };
74
75 static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = {
76 [CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq",
77 [CPUINFO_MIN_FREQ] = "cpuinfo_min_freq",
78 [CPUINFO_MAX_FREQ] = "cpuinfo_max_freq",
79 [CPUINFO_LATENCY] = "cpuinfo_transition_latency",
80 [SCALING_CUR_FREQ] = "scaling_cur_freq",
81 [SCALING_MIN_FREQ] = "scaling_min_freq",
82 [SCALING_MAX_FREQ] = "scaling_max_freq",
83 [STATS_NUM_TRANSITIONS] = "stats/total_trans"
84 };
85
cpufreq_get_sysfs_value_from_table(unsigned int cpu,const char ** table,unsigned int index,unsigned int size)86 unsigned long cpufreq_get_sysfs_value_from_table(unsigned int cpu,
87 const char **table,
88 unsigned int index,
89 unsigned int size)
90 {
91 unsigned long value;
92 unsigned int len;
93 char linebuf[MAX_LINE_LEN];
94 char *endp;
95
96 if (!table || index >= size || !table[index])
97 return 0;
98
99 len = sysfs_cpufreq_read_file(cpu, table[index], linebuf,
100 sizeof(linebuf));
101
102 if (len == 0)
103 return 0;
104
105 if (!strcmp(linebuf, "enabled\n"))
106 return 1;
107 if (!strcmp(linebuf, "disabled\n"))
108 return 0;
109 value = strtoul(linebuf, &endp, 0);
110
111 if (endp == linebuf || errno == ERANGE)
112 return 0;
113
114 return value;
115 }
116
sysfs_cpufreq_get_one_value(unsigned int cpu,enum cpufreq_value which)117 static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu,
118 enum cpufreq_value which)
119 {
120 return cpufreq_get_sysfs_value_from_table(cpu, cpufreq_value_files,
121 which,
122 MAX_CPUFREQ_VALUE_READ_FILES);
123 }
124
125 /* read access to files which contain one string */
126
127 enum cpufreq_string {
128 SCALING_DRIVER,
129 SCALING_GOVERNOR,
130 ENERGY_PERFORMANCE_PREFERENCE,
131 MAX_CPUFREQ_STRING_FILES
132 };
133
134 static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
135 [SCALING_DRIVER] = "scaling_driver",
136 [SCALING_GOVERNOR] = "scaling_governor",
137 [ENERGY_PERFORMANCE_PREFERENCE] = "energy_performance_preference",
138 };
139
140
sysfs_cpufreq_get_one_string(unsigned int cpu,enum cpufreq_string which)141 static char *sysfs_cpufreq_get_one_string(unsigned int cpu,
142 enum cpufreq_string which)
143 {
144 char linebuf[MAX_LINE_LEN];
145 char *result;
146 unsigned int len;
147
148 if (which >= MAX_CPUFREQ_STRING_FILES)
149 return NULL;
150
151 len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],
152 linebuf, sizeof(linebuf));
153 if (len == 0)
154 return NULL;
155
156 result = strdup(linebuf);
157 if (result == NULL)
158 return NULL;
159
160 if (result[strlen(result) - 1] == '\n')
161 result[strlen(result) - 1] = '\0';
162
163 return result;
164 }
165
166 /* write access */
167
168 enum cpufreq_write {
169 WRITE_SCALING_MIN_FREQ,
170 WRITE_SCALING_MAX_FREQ,
171 WRITE_SCALING_GOVERNOR,
172 WRITE_SCALING_SET_SPEED,
173 MAX_CPUFREQ_WRITE_FILES
174 };
175
176 static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = {
177 [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq",
178 [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq",
179 [WRITE_SCALING_GOVERNOR] = "scaling_governor",
180 [WRITE_SCALING_SET_SPEED] = "scaling_setspeed",
181 };
182
sysfs_cpufreq_write_one_value(unsigned int cpu,enum cpufreq_write which,const char * new_value,size_t len)183 static int sysfs_cpufreq_write_one_value(unsigned int cpu,
184 enum cpufreq_write which,
185 const char *new_value, size_t len)
186 {
187 if (which >= MAX_CPUFREQ_WRITE_FILES)
188 return 0;
189
190 if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],
191 new_value, len) != len)
192 return -ENODEV;
193
194 return 0;
195 };
196
cpufreq_get_freq_kernel(unsigned int cpu)197 unsigned long cpufreq_get_freq_kernel(unsigned int cpu)
198 {
199 return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
200 }
201
cpufreq_get_freq_hardware(unsigned int cpu)202 unsigned long cpufreq_get_freq_hardware(unsigned int cpu)
203 {
204 return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
205 }
206
cpufreq_get_transition_latency(unsigned int cpu)207 unsigned long cpufreq_get_transition_latency(unsigned int cpu)
208 {
209 return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
210 }
211
cpufreq_get_energy_performance_preference(unsigned int cpu)212 char *cpufreq_get_energy_performance_preference(unsigned int cpu)
213 {
214 return sysfs_cpufreq_get_one_string(cpu, ENERGY_PERFORMANCE_PREFERENCE);
215 }
216
cpufreq_put_energy_performance_preference(char * ptr)217 void cpufreq_put_energy_performance_preference(char *ptr)
218 {
219 if (!ptr)
220 return;
221 free(ptr);
222 }
223
cpufreq_get_hardware_limits(unsigned int cpu,unsigned long * min,unsigned long * max)224 int cpufreq_get_hardware_limits(unsigned int cpu,
225 unsigned long *min,
226 unsigned long *max)
227 {
228 if ((!min) || (!max))
229 return -EINVAL;
230
231 *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ);
232 if (!*min)
233 return -ENODEV;
234
235 *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ);
236 if (!*max)
237 return -ENODEV;
238
239 return 0;
240 }
241
cpufreq_get_driver(unsigned int cpu)242 char *cpufreq_get_driver(unsigned int cpu)
243 {
244 return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
245 }
246
cpufreq_put_driver(char * ptr)247 void cpufreq_put_driver(char *ptr)
248 {
249 if (!ptr)
250 return;
251 free(ptr);
252 }
253
cpufreq_get_policy(unsigned int cpu)254 struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu)
255 {
256 struct cpufreq_policy *policy;
257
258 policy = malloc(sizeof(struct cpufreq_policy));
259 if (!policy)
260 return NULL;
261
262 policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR);
263 if (!policy->governor) {
264 free(policy);
265 return NULL;
266 }
267 policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
268 policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ);
269 if ((!policy->min) || (!policy->max)) {
270 free(policy->governor);
271 free(policy);
272 return NULL;
273 }
274
275 return policy;
276 }
277
cpufreq_put_policy(struct cpufreq_policy * policy)278 void cpufreq_put_policy(struct cpufreq_policy *policy)
279 {
280 if ((!policy) || (!policy->governor))
281 return;
282
283 free(policy->governor);
284 policy->governor = NULL;
285 free(policy);
286 }
287
cpufreq_get_available_governors(unsigned int cpu)288 struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned
289 int cpu)
290 {
291 struct cpufreq_available_governors *first = NULL;
292 struct cpufreq_available_governors *current = NULL;
293 char linebuf[MAX_LINE_LEN];
294 unsigned int pos, i;
295 unsigned int len;
296
297 len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",
298 linebuf, sizeof(linebuf));
299 if (len == 0)
300 return NULL;
301
302 pos = 0;
303 for (i = 0; i < len; i++) {
304 if (linebuf[i] == ' ' || linebuf[i] == '\n') {
305 if (i - pos < 2)
306 continue;
307 if (current) {
308 current->next = malloc(sizeof(*current));
309 if (!current->next)
310 goto error_out;
311 current = current->next;
312 } else {
313 first = malloc(sizeof(*first));
314 if (!first)
315 return NULL;
316 current = first;
317 }
318 current->first = first;
319 current->next = NULL;
320
321 current->governor = malloc(i - pos + 1);
322 if (!current->governor)
323 goto error_out;
324
325 memcpy(current->governor, linebuf + pos, i - pos);
326 current->governor[i - pos] = '\0';
327 pos = i + 1;
328 }
329 }
330
331 return first;
332
333 error_out:
334 while (first) {
335 current = first->next;
336 if (first->governor)
337 free(first->governor);
338 free(first);
339 first = current;
340 }
341 return NULL;
342 }
343
cpufreq_put_available_governors(struct cpufreq_available_governors * any)344 void cpufreq_put_available_governors(struct cpufreq_available_governors *any)
345 {
346 struct cpufreq_available_governors *tmp, *next;
347
348 if (!any)
349 return;
350
351 tmp = any->first;
352 while (tmp) {
353 next = tmp->next;
354 if (tmp->governor)
355 free(tmp->governor);
356 free(tmp);
357 tmp = next;
358 }
359 }
360
361
362 struct cpufreq_available_frequencies
cpufreq_get_available_frequencies(unsigned int cpu)363 *cpufreq_get_available_frequencies(unsigned int cpu)
364 {
365 struct cpufreq_available_frequencies *first = NULL;
366 struct cpufreq_available_frequencies *current = NULL;
367 char one_value[SYSFS_PATH_MAX];
368 char linebuf[MAX_LINE_LEN];
369 unsigned int pos, i;
370 unsigned int len;
371
372 len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies",
373 linebuf, sizeof(linebuf));
374 if (len == 0)
375 return NULL;
376
377 pos = 0;
378 for (i = 0; i < len; i++) {
379 if (linebuf[i] == ' ' || linebuf[i] == '\n') {
380 if (i - pos < 2)
381 continue;
382 if (i - pos >= SYSFS_PATH_MAX)
383 goto error_out;
384 if (current) {
385 current->next = malloc(sizeof(*current));
386 if (!current->next)
387 goto error_out;
388 current = current->next;
389 } else {
390 first = malloc(sizeof(*first));
391 if (!first)
392 return NULL;
393 current = first;
394 }
395 current->first = first;
396 current->next = NULL;
397
398 memcpy(one_value, linebuf + pos, i - pos);
399 one_value[i - pos] = '\0';
400 if (sscanf(one_value, "%lu", ¤t->frequency) != 1)
401 goto error_out;
402
403 pos = i + 1;
404 }
405 }
406
407 return first;
408
409 error_out:
410 while (first) {
411 current = first->next;
412 free(first);
413 first = current;
414 }
415 return NULL;
416 }
417
418 struct cpufreq_available_frequencies
cpufreq_get_boost_frequencies(unsigned int cpu)419 *cpufreq_get_boost_frequencies(unsigned int cpu)
420 {
421 struct cpufreq_available_frequencies *first = NULL;
422 struct cpufreq_available_frequencies *current = NULL;
423 char one_value[SYSFS_PATH_MAX];
424 char linebuf[MAX_LINE_LEN];
425 unsigned int pos, i;
426 unsigned int len;
427
428 len = sysfs_cpufreq_read_file(cpu, "scaling_boost_frequencies",
429 linebuf, sizeof(linebuf));
430 if (len == 0)
431 return NULL;
432
433 pos = 0;
434 for (i = 0; i < len; i++) {
435 if (linebuf[i] == ' ' || linebuf[i] == '\n') {
436 if (i - pos < 2)
437 continue;
438 if (i - pos >= SYSFS_PATH_MAX)
439 goto error_out;
440 if (current) {
441 current->next = malloc(sizeof(*current));
442 if (!current->next)
443 goto error_out;
444 current = current->next;
445 } else {
446 first = malloc(sizeof(*first));
447 if (!first)
448 return NULL;
449 current = first;
450 }
451 current->first = first;
452 current->next = NULL;
453
454 memcpy(one_value, linebuf + pos, i - pos);
455 one_value[i - pos] = '\0';
456 if (sscanf(one_value, "%lu", ¤t->frequency) != 1)
457 goto error_out;
458
459 pos = i + 1;
460 }
461 }
462
463 return first;
464
465 error_out:
466 while (first) {
467 current = first->next;
468 free(first);
469 first = current;
470 }
471 return NULL;
472 }
473
cpufreq_put_available_frequencies(struct cpufreq_available_frequencies * any)474 void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies *any)
475 {
476 struct cpufreq_available_frequencies *tmp, *next;
477
478 if (!any)
479 return;
480
481 tmp = any->first;
482 while (tmp) {
483 next = tmp->next;
484 free(tmp);
485 tmp = next;
486 }
487 }
488
cpufreq_put_boost_frequencies(struct cpufreq_available_frequencies * any)489 void cpufreq_put_boost_frequencies(struct cpufreq_available_frequencies *any)
490 {
491 cpufreq_put_available_frequencies(any);
492 }
493
sysfs_get_cpu_list(unsigned int cpu,const char * file)494 static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu,
495 const char *file)
496 {
497 struct cpufreq_affected_cpus *first = NULL;
498 struct cpufreq_affected_cpus *current = NULL;
499 char one_value[SYSFS_PATH_MAX];
500 char linebuf[MAX_LINE_LEN];
501 unsigned int pos, i;
502 unsigned int len;
503
504 len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf));
505 if (len == 0)
506 return NULL;
507
508 pos = 0;
509 for (i = 0; i < len; i++) {
510 if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') {
511 if (i - pos < 1)
512 continue;
513 if (i - pos >= SYSFS_PATH_MAX)
514 goto error_out;
515 if (current) {
516 current->next = malloc(sizeof(*current));
517 if (!current->next)
518 goto error_out;
519 current = current->next;
520 } else {
521 first = malloc(sizeof(*first));
522 if (!first)
523 return NULL;
524 current = first;
525 }
526 current->first = first;
527 current->next = NULL;
528
529 memcpy(one_value, linebuf + pos, i - pos);
530 one_value[i - pos] = '\0';
531
532 if (sscanf(one_value, "%u", ¤t->cpu) != 1)
533 goto error_out;
534
535 pos = i + 1;
536 }
537 }
538
539 return first;
540
541 error_out:
542 while (first) {
543 current = first->next;
544 free(first);
545 first = current;
546 }
547 return NULL;
548 }
549
cpufreq_get_affected_cpus(unsigned int cpu)550 struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu)
551 {
552 return sysfs_get_cpu_list(cpu, "affected_cpus");
553 }
554
cpufreq_put_affected_cpus(struct cpufreq_affected_cpus * any)555 void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any)
556 {
557 struct cpufreq_affected_cpus *tmp, *next;
558
559 if (!any)
560 return;
561
562 tmp = any->first;
563 while (tmp) {
564 next = tmp->next;
565 free(tmp);
566 tmp = next;
567 }
568 }
569
570
cpufreq_get_related_cpus(unsigned int cpu)571 struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu)
572 {
573 return sysfs_get_cpu_list(cpu, "related_cpus");
574 }
575
cpufreq_put_related_cpus(struct cpufreq_affected_cpus * any)576 void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any)
577 {
578 cpufreq_put_affected_cpus(any);
579 }
580
verify_gov(char * new_gov,char * passed_gov)581 static int verify_gov(char *new_gov, char *passed_gov)
582 {
583 unsigned int i, j = 0;
584
585 if (!passed_gov || (strlen(passed_gov) > 19))
586 return -EINVAL;
587
588 strncpy(new_gov, passed_gov, 20);
589 for (i = 0; i < 20; i++) {
590 if (j) {
591 new_gov[i] = '\0';
592 continue;
593 }
594 if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z'))
595 continue;
596
597 if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z'))
598 continue;
599
600 if (new_gov[i] == '-')
601 continue;
602
603 if (new_gov[i] == '_')
604 continue;
605
606 if (new_gov[i] == '\0') {
607 j = 1;
608 continue;
609 }
610 return -EINVAL;
611 }
612 new_gov[19] = '\0';
613 return 0;
614 }
615
cpufreq_set_policy(unsigned int cpu,struct cpufreq_policy * policy)616 int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy)
617 {
618 char min[SYSFS_PATH_MAX];
619 char max[SYSFS_PATH_MAX];
620 char gov[SYSFS_PATH_MAX];
621 int ret;
622 unsigned long old_min;
623 int write_max_first;
624
625 if (!policy || !(policy->governor))
626 return -EINVAL;
627
628 if (policy->max < policy->min)
629 return -EINVAL;
630
631 if (verify_gov(gov, policy->governor))
632 return -EINVAL;
633
634 snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min);
635 snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max);
636
637 old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
638 write_max_first = (old_min && (policy->max < old_min) ? 0 : 1);
639
640 if (write_max_first) {
641 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
642 max, strlen(max));
643 if (ret)
644 return ret;
645 }
646
647 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,
648 strlen(min));
649 if (ret)
650 return ret;
651
652 if (!write_max_first) {
653 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
654 max, strlen(max));
655 if (ret)
656 return ret;
657 }
658
659 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
660 gov, strlen(gov));
661 }
662
663
cpufreq_modify_policy_min(unsigned int cpu,unsigned long min_freq)664 int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq)
665 {
666 char value[SYSFS_PATH_MAX];
667
668 snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq);
669
670 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ,
671 value, strlen(value));
672 }
673
674
cpufreq_modify_policy_max(unsigned int cpu,unsigned long max_freq)675 int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq)
676 {
677 char value[SYSFS_PATH_MAX];
678
679 snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq);
680
681 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
682 value, strlen(value));
683 }
684
cpufreq_modify_policy_governor(unsigned int cpu,char * governor)685 int cpufreq_modify_policy_governor(unsigned int cpu, char *governor)
686 {
687 char new_gov[SYSFS_PATH_MAX];
688
689 if ((!governor) || (strlen(governor) > 19))
690 return -EINVAL;
691
692 if (verify_gov(new_gov, governor))
693 return -EINVAL;
694
695 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
696 new_gov, strlen(new_gov));
697 }
698
cpufreq_set_frequency(unsigned int cpu,unsigned long target_frequency)699 int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency)
700 {
701 struct cpufreq_policy *pol = cpufreq_get_policy(cpu);
702 char userspace_gov[] = "userspace";
703 char freq[SYSFS_PATH_MAX];
704 int ret;
705
706 if (!pol)
707 return -ENODEV;
708
709 if (strncmp(pol->governor, userspace_gov, 9) != 0) {
710 ret = cpufreq_modify_policy_governor(cpu, userspace_gov);
711 if (ret) {
712 cpufreq_put_policy(pol);
713 return ret;
714 }
715 }
716
717 cpufreq_put_policy(pol);
718
719 snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);
720
721 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,
722 freq, strlen(freq));
723 }
724
cpufreq_get_stats(unsigned int cpu,unsigned long long * total_time)725 struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu,
726 unsigned long long *total_time)
727 {
728 struct cpufreq_stats *first = NULL;
729 struct cpufreq_stats *current = NULL;
730 char one_value[SYSFS_PATH_MAX];
731 char linebuf[MAX_LINE_LEN];
732 unsigned int pos, i;
733 unsigned int len;
734
735 len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
736 linebuf, sizeof(linebuf));
737 if (len == 0)
738 return NULL;
739
740 *total_time = 0;
741 pos = 0;
742 for (i = 0; i < len; i++) {
743 if (i == strlen(linebuf) || linebuf[i] == '\n') {
744 if (i - pos < 2)
745 continue;
746 if ((i - pos) >= SYSFS_PATH_MAX)
747 goto error_out;
748 if (current) {
749 current->next = malloc(sizeof(*current));
750 if (!current->next)
751 goto error_out;
752 current = current->next;
753 } else {
754 first = malloc(sizeof(*first));
755 if (!first)
756 return NULL;
757 current = first;
758 }
759 current->first = first;
760 current->next = NULL;
761
762 memcpy(one_value, linebuf + pos, i - pos);
763 one_value[i - pos] = '\0';
764 if (sscanf(one_value, "%lu %llu",
765 ¤t->frequency,
766 ¤t->time_in_state) != 2)
767 goto error_out;
768
769 *total_time = *total_time + current->time_in_state;
770 pos = i + 1;
771 }
772 }
773
774 return first;
775
776 error_out:
777 while (first) {
778 current = first->next;
779 free(first);
780 first = current;
781 }
782 return NULL;
783 }
784
cpufreq_put_stats(struct cpufreq_stats * any)785 void cpufreq_put_stats(struct cpufreq_stats *any)
786 {
787 struct cpufreq_stats *tmp, *next;
788
789 if (!any)
790 return;
791
792 tmp = any->first;
793 while (tmp) {
794 next = tmp->next;
795 free(tmp);
796 tmp = next;
797 }
798 }
799
cpufreq_get_transitions(unsigned int cpu)800 unsigned long cpufreq_get_transitions(unsigned int cpu)
801 {
802 return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
803 }
804