1
2
3
4
5
6
7
8#include <unistd.h>
9#include <stdio.h>
10#include <errno.h>
11#include <stdlib.h>
12#include <limits.h>
13#include <string.h>
14#include <ctype.h>
15
16#include <getopt.h>
17
18#include "cpufreq.h"
19#include "cpuidle.h"
20#include "helpers/helpers.h"
21
22#define NORM_FREQ_LEN 32
23
24static struct option set_opts[] = {
25 {"min", required_argument, NULL, 'd'},
26 {"max", required_argument, NULL, 'u'},
27 {"governor", required_argument, NULL, 'g'},
28 {"freq", required_argument, NULL, 'f'},
29 {"related", no_argument, NULL, 'r'},
30 { },
31};
32
33static void print_error(void)
34{
35 printf(_("Error setting new values. Common errors:\n"
36 "- Do you have proper administration rights? (super-user?)\n"
37 "- Is the governor you requested available and modprobed?\n"
38 "- Trying to set an invalid policy?\n"
39 "- Trying to set a specific frequency, but userspace governor is not available,\n"
40 " for example because of hardware which cannot be set to a specific frequency\n"
41 " or because the userspace governor isn't loaded?\n"));
42};
43
44struct freq_units {
45 char *str_unit;
46 int power_of_ten;
47};
48
49const struct freq_units def_units[] = {
50 {"hz", -3},
51 {"khz", 0},
52 {"mhz", 3},
53 {"ghz", 6},
54 {"thz", 9},
55 {NULL, 0}
56};
57
58static void print_unknown_arg(void)
59{
60 printf(_("invalid or unknown argument\n"));
61}
62
63static unsigned long string_to_frequency(const char *str)
64{
65 char normalized[NORM_FREQ_LEN];
66 const struct freq_units *unit;
67 const char *scan;
68 char *end;
69 unsigned long freq;
70 int power = 0, match_count = 0, i, cp, pad;
71
72 while (*str == '0')
73 str++;
74
75 for (scan = str; isdigit(*scan) || *scan == '.'; scan++) {
76 if (*scan == '.' && match_count == 0)
77 match_count = 1;
78 else if (*scan == '.' && match_count == 1)
79 return 0;
80 }
81
82 if (*scan) {
83 match_count = 0;
84 for (unit = def_units; unit->str_unit; unit++) {
85 for (i = 0;
86 scan[i] && tolower(scan[i]) == unit->str_unit[i];
87 ++i)
88 continue;
89 if (scan[i])
90 continue;
91 match_count++;
92 power = unit->power_of_ten;
93 }
94 if (match_count != 1)
95 return 0;
96 }
97
98
99 for (cp = 0; isdigit(str[cp]); cp++)
100 continue;
101
102 if (str[cp] == '.') {
103 while (power > -1 && isdigit(str[cp+1]))
104 cp++, power--;
105 }
106 if (power >= -1)
107 pad = power + 1;
108 else
109 pad = 0, cp += power + 1;
110
111 if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1)
112 return 0;
113
114
115 for (i = 0; i < cp; i++, str++) {
116 if (*str == '.')
117 str++;
118 normalized[i] = *str;
119 }
120
121 for (; i < cp + pad; i++)
122 normalized[i] = '0';
123
124
125 match_count = (normalized[i-1] >= '5');
126
127 normalized[i-1] = 0;
128
129
130 errno = 0;
131 freq = strtoul(normalized, &end, 10);
132 if (errno)
133 return 0;
134 else {
135 if (match_count && freq != ULONG_MAX)
136 freq++;
137 return freq;
138 }
139}
140
141static int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol)
142{
143 struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu);
144 int ret;
145
146 if (!cur_pol) {
147 printf(_("wrong, unknown or unhandled CPU?\n"));
148 return -EINVAL;
149 }
150
151 if (!new_pol->min)
152 new_pol->min = cur_pol->min;
153
154 if (!new_pol->max)
155 new_pol->max = cur_pol->max;
156
157 if (!new_pol->governor)
158 new_pol->governor = cur_pol->governor;
159
160 ret = cpufreq_set_policy(cpu, new_pol);
161
162 cpufreq_put_policy(cur_pol);
163
164 return ret;
165}
166
167
168static int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol,
169 unsigned long freq, unsigned int pc)
170{
171 switch (pc) {
172 case 0:
173 return cpufreq_set_frequency(cpu, freq);
174
175 case 1:
176
177
178
179 if (new_pol->min)
180 return cpufreq_modify_policy_min(cpu, new_pol->min);
181 else if (new_pol->max)
182 return cpufreq_modify_policy_max(cpu, new_pol->max);
183 else if (new_pol->governor)
184 return cpufreq_modify_policy_governor(cpu,
185 new_pol->governor);
186
187 default:
188
189 return do_new_policy(cpu, new_pol);
190 }
191}
192
193int cmd_freq_set(int argc, char **argv)
194{
195 extern char *optarg;
196 extern int optind, opterr, optopt;
197 int ret = 0, cont = 1;
198 int double_parm = 0, related = 0, policychange = 0;
199 unsigned long freq = 0;
200 char gov[20];
201 unsigned int cpu;
202
203 struct cpufreq_policy new_pol = {
204 .min = 0,
205 .max = 0,
206 .governor = NULL,
207 };
208
209
210 do {
211 ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL);
212 switch (ret) {
213 case '?':
214 print_unknown_arg();
215 return -EINVAL;
216 case -1:
217 cont = 0;
218 break;
219 case 'r':
220 if (related)
221 double_parm++;
222 related++;
223 break;
224 case 'd':
225 if (new_pol.min)
226 double_parm++;
227 policychange++;
228 new_pol.min = string_to_frequency(optarg);
229 if (new_pol.min == 0) {
230 print_unknown_arg();
231 return -EINVAL;
232 }
233 break;
234 case 'u':
235 if (new_pol.max)
236 double_parm++;
237 policychange++;
238 new_pol.max = string_to_frequency(optarg);
239 if (new_pol.max == 0) {
240 print_unknown_arg();
241 return -EINVAL;
242 }
243 break;
244 case 'f':
245 if (freq)
246 double_parm++;
247 freq = string_to_frequency(optarg);
248 if (freq == 0) {
249 print_unknown_arg();
250 return -EINVAL;
251 }
252 break;
253 case 'g':
254 if (new_pol.governor)
255 double_parm++;
256 policychange++;
257 if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) {
258 print_unknown_arg();
259 return -EINVAL;
260 }
261 if ((sscanf(optarg, "%19s", gov)) != 1) {
262 print_unknown_arg();
263 return -EINVAL;
264 }
265 new_pol.governor = gov;
266 break;
267 }
268 } while (cont);
269
270
271 if (double_parm) {
272 printf("the same parameter was passed more than once\n");
273 return -EINVAL;
274 }
275
276 if (freq && policychange) {
277 printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n"
278 "-g/--governor parameters\n"));
279 return -EINVAL;
280 }
281
282 if (!freq && !policychange) {
283 printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n"
284 "-g/--governor must be passed\n"));
285 return -EINVAL;
286 }
287
288
289 if (bitmask_isallclear(cpus_chosen))
290 bitmask_setall(cpus_chosen);
291
292
293 if (related) {
294 for (cpu = bitmask_first(cpus_chosen);
295 cpu <= bitmask_last(cpus_chosen); cpu++) {
296 struct cpufreq_affected_cpus *cpus;
297
298 if (!bitmask_isbitset(cpus_chosen, cpu) ||
299 cpupower_is_cpu_online(cpu))
300 continue;
301
302 cpus = cpufreq_get_related_cpus(cpu);
303 if (!cpus)
304 break;
305 while (cpus->next) {
306 bitmask_setbit(cpus_chosen, cpus->cpu);
307 cpus = cpus->next;
308 }
309 cpufreq_put_related_cpus(cpus);
310 }
311 }
312
313
314
315 for (cpu = bitmask_first(cpus_chosen);
316 cpu <= bitmask_last(cpus_chosen); cpu++) {
317
318 if (!bitmask_isbitset(cpus_chosen, cpu) ||
319 cpupower_is_cpu_online(cpu))
320 continue;
321
322 if (cpupower_is_cpu_online(cpu) != 1)
323 continue;
324
325 printf(_("Setting cpu: %d\n"), cpu);
326 ret = do_one_cpu(cpu, &new_pol, freq, policychange);
327 if (ret) {
328 print_error();
329 return ret;
330 }
331 }
332
333 return 0;
334}
335