1 /*
2  *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
3  *  (C) 2010       Thomas Renninger <trenn@suse.de>
4  *
5  *  Licensed under the terms of the GNU GPL License version 2.
6  */
7 
8 
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <errno.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <getopt.h>
15 #include <cpufreq.h>
16 
17 #include "helpers/helpers.h"
18 #include "helpers/sysfs.h"
19 #include "helpers/bitmask.h"
20 
21 #define LINE_LEN 10
22 
23 static void cpuidle_cpu_output(unsigned int cpu, int verbose)
24 {
25 	int idlestates, idlestate;
26 	char *tmp;
27 
28 	printf(_ ("Analyzing CPU %d:\n"), cpu);
29 
30 	idlestates = sysfs_get_idlestate_count(cpu);
31 	if (idlestates == 0) {
32 		printf(_("CPU %u: No idle states\n"), cpu);
33 		return;
34 	}
35 	else if (idlestates <= 0) {
36 		printf(_("CPU %u: Can't read idle state info\n"), cpu);
37 		return;
38 	}
39 	tmp = sysfs_get_idlestate_name(cpu, idlestates - 1);
40 	if (!tmp) {
41 		printf(_("Could not determine max idle state %u\n"),
42 		       idlestates - 1);
43 		return;
44 	}
45 
46 	printf(_("Number of idle states: %d\n"), idlestates);
47 
48 	printf(_("Available idle states:"));
49 	for (idlestate = 1; idlestate < idlestates; idlestate++) {
50 		tmp = sysfs_get_idlestate_name(cpu, idlestate);
51 		if (!tmp)
52 			continue;
53 		printf(" %s", tmp);
54 		free(tmp);
55 	}
56 	printf("\n");
57 
58 	if (!verbose)
59 		return;
60 
61 	for (idlestate = 1; idlestate < idlestates; idlestate++) {
62 		tmp = sysfs_get_idlestate_name(cpu, idlestate);
63 		if (!tmp)
64 			continue;
65 		printf("%s:\n", tmp);
66 		free(tmp);
67 
68 		tmp = sysfs_get_idlestate_desc(cpu, idlestate);
69 		if (!tmp)
70 			continue;
71 		printf(_("Flags/Description: %s\n"), tmp);
72 		free(tmp);
73 
74 		printf(_("Latency: %lu\n"),
75 		       sysfs_get_idlestate_latency(cpu, idlestate));
76 		printf(_("Usage: %lu\n"),
77 		       sysfs_get_idlestate_usage(cpu, idlestate));
78 		printf(_("Duration: %llu\n"),
79 		       sysfs_get_idlestate_time(cpu, idlestate));
80 	}
81 	printf("\n");
82 }
83 
84 static void cpuidle_general_output(void)
85 {
86 	char *tmp;
87 
88 	tmp = sysfs_get_cpuidle_driver();
89 	if (!tmp) {
90 		printf(_("Could not determine cpuidle driver\n"));
91 		return;
92 	}
93 
94 	printf(_("CPUidle driver: %s\n"), tmp);
95 	free (tmp);
96 
97 	tmp = sysfs_get_cpuidle_governor();
98 	if (!tmp) {
99 		printf(_("Could not determine cpuidle governor\n"));
100 		return;
101 	}
102 
103 	printf(_("CPUidle governor: %s\n"), tmp);
104 	free (tmp);
105 }
106 
107 static void proc_cpuidle_cpu_output(unsigned int cpu)
108 {
109 	long max_allowed_cstate = 2000000000;
110 	int cstates, cstate;
111 
112 	cstates = sysfs_get_idlestate_count(cpu);
113 	if (cstates == 0) {
114 		/*
115 		 * Go on and print same useless info as you'd see with
116 		 * cat /proc/acpi/processor/../power
117 		 *	printf(_("CPU %u: No C-states available\n"), cpu);
118 		 *	return;
119 		 */
120 	}
121 	else if (cstates <= 0) {
122 		printf(_("CPU %u: Can't read C-state info\n"), cpu);
123 		return;
124 	}
125 	/* printf("Cstates: %d\n", cstates); */
126 
127 	printf(_("active state:            C0\n"));
128 	printf(_("max_cstate:              C%u\n"), cstates-1);
129 	printf(_("maximum allowed latency: %lu usec\n"), max_allowed_cstate);
130 	printf(_("states:\t\n"));
131 	for (cstate = 1; cstate < cstates; cstate++) {
132 		printf(_("    C%d:                  "
133 			 "type[C%d] "), cstate, cstate);
134 		printf(_("promotion[--] demotion[--] "));
135 		printf(_("latency[%03lu] "),
136 		       sysfs_get_idlestate_latency(cpu, cstate));
137 		printf(_("usage[%08lu] "),
138 		       sysfs_get_idlestate_usage(cpu, cstate));
139 		printf(_("duration[%020Lu] \n"),
140 		       sysfs_get_idlestate_time(cpu, cstate));
141 	}
142 }
143 
144 /* --freq / -f */
145 
146 void idle_info_help(void) {
147 	printf(_ ("Usage: cpupower idleinfo [options]\n"));
148 	printf(_ ("Options:\n"));
149 	printf(_ ("  -s, --silent         Only show general C-state information\n"));
150 	printf(_ ("  -o, --proc           Prints out information like provided by the /proc/acpi/processor/*/power\n"
151 	       "                       interface in older kernels\n"));
152 	printf(_ ("  -h, --help           Prints out this screen\n"));
153 
154 	printf("\n");
155 }
156 
157 static struct option info_opts[] = {
158 	{ .name="silent",	.has_arg=no_argument,		.flag=NULL,	.val='s'},
159 	{ .name="proc",		.has_arg=no_argument,		.flag=NULL,	.val='o'},
160 	{ .name="help",		.has_arg=no_argument,		.flag=NULL,	.val='h'},
161 	{ },
162 };
163 
164 static inline void cpuidle_exit(int fail)
165 {
166 	idle_info_help();
167 	exit(EXIT_FAILURE);
168 }
169 
170 int cmd_idle_info(int argc, char **argv)
171 {
172 	extern char *optarg;
173 	extern int optind, opterr, optopt;
174 	int ret = 0, cont = 1, output_param = 0, verbose = 1;
175 	unsigned int cpu = 0;
176 
177 	do {
178 		ret = getopt_long(argc, argv, "hos", info_opts, NULL);
179 		if (ret == -1)
180 			break;
181 		switch (ret) {
182 		case '?':
183 			output_param = '?';
184 			cont = 0;
185 			break;
186 		case 'h':
187 			output_param = 'h';
188 			cont = 0;
189 			break;
190 		case 's':
191 			verbose = 0;
192 			break;
193 		case -1:
194 			cont = 0;
195 			break;
196 		case 'o':
197 			if (output_param) {
198 				output_param = -1;
199 				cont = 0;
200 				break;
201 			}
202 			output_param = ret;
203 			break;
204 		}
205 	} while(cont);
206 
207 	switch (output_param) {
208 	case -1:
209 		printf(_("You can't specify more than one "
210 			 "output-specific argument\n"));
211 		cpuidle_exit(EXIT_FAILURE);
212 	case '?':
213 		printf(_("invalid or unknown argument\n"));
214 		cpuidle_exit(EXIT_FAILURE);
215 	case 'h':
216 		cpuidle_exit(EXIT_SUCCESS);
217 	}
218 
219 	/* Default is: show output of CPU 0 only */
220 	if (bitmask_isallclear(cpus_chosen))
221 		bitmask_setbit(cpus_chosen, 0);
222 
223 	if (output_param == 0)
224 		cpuidle_general_output();
225 
226 	for (cpu = bitmask_first(cpus_chosen);
227 	     cpu <= bitmask_last(cpus_chosen); cpu++) {
228 
229 		if (!bitmask_isbitset(cpus_chosen, cpu) ||
230 		    cpufreq_cpu_exists(cpu))
231 			continue;
232 
233 		switch (output_param) {
234 
235 		case 'o':
236 			proc_cpuidle_cpu_output(cpu);
237 			break;
238 		case 0:
239 			printf("\n");
240 			cpuidle_cpu_output(cpu, verbose);
241 			break;
242 		}
243 	}
244 	return (EXIT_SUCCESS);
245 }
246