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