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 #include "helpers/sysfs.h" 21 22 #define NORM_FREQ_LEN 32 23 24 static struct option set_opts[] = { 25 { .name = "min", .has_arg = required_argument, .flag = NULL, .val = 'd'}, 26 { .name = "max", .has_arg = required_argument, .flag = NULL, .val = 'u'}, 27 { .name = "governor", .has_arg = required_argument, .flag = NULL, .val = 'g'}, 28 { .name = "freq", .has_arg = required_argument, .flag = NULL, .val = 'f'}, 29 { .name = "related", .has_arg = no_argument, .flag = NULL, .val='r'}, 30 { }, 31 }; 32 33 static 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 44 struct freq_units { 45 char *str_unit; 46 int power_of_ten; 47 }; 48 49 const struct freq_units def_units[] = { 50 {"hz", -3}, 51 {"khz", 0}, /* default */ 52 {"mhz", 3}, 53 {"ghz", 6}, 54 {"thz", 9}, 55 {NULL, 0} 56 }; 57 58 static void print_unknown_arg(void) 59 { 60 printf(_("invalid or unknown argument\n")); 61 } 62 63 static 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 /* count the number of digits to be copied */ 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) /* not enough => pad */ 107 pad = power + 1; 108 else /* to much => strip */ 109 pad = 0, cp += power + 1; 110 /* check bounds */ 111 if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1) 112 return 0; 113 114 /* copy digits */ 115 for (i = 0; i < cp; i++, str++) { 116 if (*str == '.') 117 str++; 118 normalized[i] = *str; 119 } 120 /* and pad */ 121 for (; i < cp + pad; i++) 122 normalized[i] = '0'; 123 124 /* round up, down ? */ 125 match_count = (normalized[i-1] >= '5'); 126 /* and drop the decimal part */ 127 normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */ 128 129 /* final conversion (and applying rounding) */ 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 141 static 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 168 static 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 /* if only one value of a policy is to be changed, we can 177 * use a "fast path". 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 /* slow path */ 189 return do_new_policy(cpu, new_pol); 190 } 191 } 192 193 int 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 /* parameter parsing */ 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 /* parameter checking */ 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 /* Default is: set all CPUs */ 289 if (bitmask_isallclear(cpus_chosen)) 290 bitmask_setall(cpus_chosen); 291 292 /* Also set frequency settings for related CPUs if -r is passed */ 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 cpufreq_cpu_exists(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 /* loop over CPUs */ 315 for (cpu = bitmask_first(cpus_chosen); 316 cpu <= bitmask_last(cpus_chosen); cpu++) { 317 318 if (!bitmask_isbitset(cpus_chosen, cpu) || 319 cpufreq_cpu_exists(cpu)) 320 continue; 321 322 if (sysfs_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