123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- /*
- * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
- *
- * Licensed under the terms of the GNU GPL License version 2.
- */
- #include <unistd.h>
- #include <stdio.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <limits.h>
- #include <string.h>
- #include <ctype.h>
- #include <getopt.h>
- #include "cpufreq.h"
- #include "helpers/helpers.h"
- #include "helpers/sysfs.h"
- #define NORM_FREQ_LEN 32
- static struct option set_opts[] = {
- {"min", required_argument, NULL, 'd'},
- {"max", required_argument, NULL, 'u'},
- {"governor", required_argument, NULL, 'g'},
- {"freq", required_argument, NULL, 'f'},
- {"related", no_argument, NULL, 'r'},
- { },
- };
- static void print_error(void)
- {
- printf(_("Error setting new values. Common errors:\n"
- "- Do you have proper administration rights? (super-user?)\n"
- "- Is the governor you requested available and modprobed?\n"
- "- Trying to set an invalid policy?\n"
- "- Trying to set a specific frequency, but userspace governor is not available,\n"
- " for example because of hardware which cannot be set to a specific frequency\n"
- " or because the userspace governor isn't loaded?\n"));
- };
- struct freq_units {
- char *str_unit;
- int power_of_ten;
- };
- const struct freq_units def_units[] = {
- {"hz", -3},
- {"khz", 0}, /* default */
- {"mhz", 3},
- {"ghz", 6},
- {"thz", 9},
- {NULL, 0}
- };
- static void print_unknown_arg(void)
- {
- printf(_("invalid or unknown argument\n"));
- }
- static unsigned long string_to_frequency(const char *str)
- {
- char normalized[NORM_FREQ_LEN];
- const struct freq_units *unit;
- const char *scan;
- char *end;
- unsigned long freq;
- int power = 0, match_count = 0, i, cp, pad;
- while (*str == '0')
- str++;
- for (scan = str; isdigit(*scan) || *scan == '.'; scan++) {
- if (*scan == '.' && match_count == 0)
- match_count = 1;
- else if (*scan == '.' && match_count == 1)
- return 0;
- }
- if (*scan) {
- match_count = 0;
- for (unit = def_units; unit->str_unit; unit++) {
- for (i = 0;
- scan[i] && tolower(scan[i]) == unit->str_unit[i];
- ++i)
- continue;
- if (scan[i])
- continue;
- match_count++;
- power = unit->power_of_ten;
- }
- if (match_count != 1)
- return 0;
- }
- /* count the number of digits to be copied */
- for (cp = 0; isdigit(str[cp]); cp++)
- continue;
- if (str[cp] == '.') {
- while (power > -1 && isdigit(str[cp+1]))
- cp++, power--;
- }
- if (power >= -1) /* not enough => pad */
- pad = power + 1;
- else /* to much => strip */
- pad = 0, cp += power + 1;
- /* check bounds */
- if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1)
- return 0;
- /* copy digits */
- for (i = 0; i < cp; i++, str++) {
- if (*str == '.')
- str++;
- normalized[i] = *str;
- }
- /* and pad */
- for (; i < cp + pad; i++)
- normalized[i] = '0';
- /* round up, down ? */
- match_count = (normalized[i-1] >= '5');
- /* and drop the decimal part */
- normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */
- /* final conversion (and applying rounding) */
- errno = 0;
- freq = strtoul(normalized, &end, 10);
- if (errno)
- return 0;
- else {
- if (match_count && freq != ULONG_MAX)
- freq++;
- return freq;
- }
- }
- static int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol)
- {
- struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu);
- int ret;
- if (!cur_pol) {
- printf(_("wrong, unknown or unhandled CPU?\n"));
- return -EINVAL;
- }
- if (!new_pol->min)
- new_pol->min = cur_pol->min;
- if (!new_pol->max)
- new_pol->max = cur_pol->max;
- if (!new_pol->governor)
- new_pol->governor = cur_pol->governor;
- ret = cpufreq_set_policy(cpu, new_pol);
- cpufreq_put_policy(cur_pol);
- return ret;
- }
- static int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol,
- unsigned long freq, unsigned int pc)
- {
- switch (pc) {
- case 0:
- return cpufreq_set_frequency(cpu, freq);
- case 1:
- /* if only one value of a policy is to be changed, we can
- * use a "fast path".
- */
- if (new_pol->min)
- return cpufreq_modify_policy_min(cpu, new_pol->min);
- else if (new_pol->max)
- return cpufreq_modify_policy_max(cpu, new_pol->max);
- else if (new_pol->governor)
- return cpufreq_modify_policy_governor(cpu,
- new_pol->governor);
- default:
- /* slow path */
- return do_new_policy(cpu, new_pol);
- }
- }
- int cmd_freq_set(int argc, char **argv)
- {
- extern char *optarg;
- extern int optind, opterr, optopt;
- int ret = 0, cont = 1;
- int double_parm = 0, related = 0, policychange = 0;
- unsigned long freq = 0;
- char gov[20];
- unsigned int cpu;
- struct cpufreq_policy new_pol = {
- .min = 0,
- .max = 0,
- .governor = NULL,
- };
- /* parameter parsing */
- do {
- ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL);
- switch (ret) {
- case '?':
- print_unknown_arg();
- return -EINVAL;
- case -1:
- cont = 0;
- break;
- case 'r':
- if (related)
- double_parm++;
- related++;
- break;
- case 'd':
- if (new_pol.min)
- double_parm++;
- policychange++;
- new_pol.min = string_to_frequency(optarg);
- if (new_pol.min == 0) {
- print_unknown_arg();
- return -EINVAL;
- }
- break;
- case 'u':
- if (new_pol.max)
- double_parm++;
- policychange++;
- new_pol.max = string_to_frequency(optarg);
- if (new_pol.max == 0) {
- print_unknown_arg();
- return -EINVAL;
- }
- break;
- case 'f':
- if (freq)
- double_parm++;
- freq = string_to_frequency(optarg);
- if (freq == 0) {
- print_unknown_arg();
- return -EINVAL;
- }
- break;
- case 'g':
- if (new_pol.governor)
- double_parm++;
- policychange++;
- if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) {
- print_unknown_arg();
- return -EINVAL;
- }
- if ((sscanf(optarg, "%19s", gov)) != 1) {
- print_unknown_arg();
- return -EINVAL;
- }
- new_pol.governor = gov;
- break;
- }
- } while (cont);
- /* parameter checking */
- if (double_parm) {
- printf("the same parameter was passed more than once\n");
- return -EINVAL;
- }
- if (freq && policychange) {
- printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n"
- "-g/--governor parameters\n"));
- return -EINVAL;
- }
- if (!freq && !policychange) {
- printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n"
- "-g/--governor must be passed\n"));
- return -EINVAL;
- }
- /* Default is: set all CPUs */
- if (bitmask_isallclear(cpus_chosen))
- bitmask_setall(cpus_chosen);
- /* Also set frequency settings for related CPUs if -r is passed */
- if (related) {
- for (cpu = bitmask_first(cpus_chosen);
- cpu <= bitmask_last(cpus_chosen); cpu++) {
- struct cpufreq_affected_cpus *cpus;
- if (!bitmask_isbitset(cpus_chosen, cpu) ||
- cpufreq_cpu_exists(cpu))
- continue;
- cpus = cpufreq_get_related_cpus(cpu);
- if (!cpus)
- break;
- while (cpus->next) {
- bitmask_setbit(cpus_chosen, cpus->cpu);
- cpus = cpus->next;
- }
- cpufreq_put_related_cpus(cpus);
- }
- }
- /* loop over CPUs */
- for (cpu = bitmask_first(cpus_chosen);
- cpu <= bitmask_last(cpus_chosen); cpu++) {
- if (!bitmask_isbitset(cpus_chosen, cpu) ||
- cpufreq_cpu_exists(cpu))
- continue;
- if (sysfs_is_cpu_online(cpu) != 1)
- continue;
- printf(_("Setting cpu: %d\n"), cpu);
- ret = do_one_cpu(cpu, &new_pol, freq, policychange);
- if (ret) {
- print_error();
- return ret;
- }
- }
- return 0;
- }
|