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