blob: 4bea7a6e744f15105b09aa5e8d3c72168e0c1fd3 [file] [log] [blame]
/*
* (C) 2004-2006 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 <libintl.h>
#include <locale.h>
#include <getopt.h>
#include "cpufreq.h"
#define _(String) gettext (String)
#define gettext_noop(String) String
#define N_(String) gettext_noop (String)
#define NORM_FREQ_LEN 32
static void print_header(void) {
printf(PACKAGE " " VERSION ": cpufreq-set (C) Dominik Brodowski 2004-2006\n");
printf(gettext ("Report errors and bugs to %s, please.\n"), PACKAGE_BUGREPORT);
}
static void print_help(void) {
printf(gettext ("Usage: cpufreq-set [options]\n"));
printf(gettext ("Options:\n"));
printf(gettext (" -c CPU, --cpu CPU number of CPU where cpufreq settings shall be modified\n"));
printf(gettext (" -d FREQ, --min FREQ new minimum CPU frequency the governor may select\n"));
printf(gettext (" -u FREQ, --max FREQ new maximum CPU frequency the governor may select\n"));
printf(gettext (" -g GOV, --governor GOV new cpufreq governor\n"));
printf(gettext (" -f FREQ, --freq FREQ specific frequency to be set. Requires userspace\n"
" governor to be available and loaded\n"));
printf(gettext (" -h, --help Prints out this screen\n"));
printf("\n");
printf(gettext ("Notes:\n"
"1. Omitting the -c or --cpu argument is equivalent to setting it to zero\n"
"2. The -f FREQ, --freq FREQ parameter cannot be combined with any other parameter\n"
" except the -c CPU, --cpu CPU parameter\n"
"3. FREQuencies can be passed in Hz, kHz (default), MHz, GHz, or THz\n"
" by postfixing the value with the wanted unit name, without any space\n"
" (FREQuency in kHz =^ Hz * 0.001 =^ MHz * 1000 =^ GHz * 1000000).\n"));
}
static struct option set_opts[] = {
{ .name="cpu", .has_arg=required_argument, .flag=NULL, .val='c'},
{ .name="min", .has_arg=required_argument, .flag=NULL, .val='d'},
{ .name="max", .has_arg=required_argument, .flag=NULL, .val='u'},
{ .name="governor", .has_arg=required_argument, .flag=NULL, .val='g'},
{ .name="freq", .has_arg=required_argument, .flag=NULL, .val='f'},
{ .name="help", .has_arg=no_argument, .flag=NULL, .val='h'},
};
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) {
print_header();
printf(gettext ("invalid or unknown argument\n"));
print_help();
}
static unsigned long string_to_frequency(const char* str)
{
char normalized[NORM_FREQ_LEN];
struct freq_units const * 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);
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++);
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;
}
}
int main(int argc, char **argv) {
extern char *optarg;
extern int optind, opterr, optopt;
int ret = 0, cont = 1;
unsigned int cpu = 0;
unsigned long min = 0;
unsigned long max = 0;
unsigned long freq = 0;
char gov[20];
int freq_is_set = 0;
int min_is_set = 0;
int max_is_set = 0;
int gov_is_set = 0;
int cpu_is_set = 0;
int double_parm = 0;
setlocale(LC_ALL, "");
textdomain (PACKAGE);
do {
ret = getopt_long(argc, argv, "c:d:u:g:f:h", set_opts, NULL);
switch (ret) {
case '?':
print_unknown_arg();
return -EINVAL;
case 'h':
print_header();
print_help();
return 0;
case -1:
cont = 0;
break;
case 'c':
if (cpu_is_set)
double_parm++;
cpu_is_set++;
if ((sscanf(optarg, "%d ", &cpu)) != 1) {
print_unknown_arg();
return -EINVAL;
}
break;
case 'd':
if (min_is_set)
double_parm++;
min_is_set++;
min = string_to_frequency(optarg);
if (min == 0) {
print_unknown_arg();
return -EINVAL;
}
break;
case 'u':
if (max_is_set)
double_parm++;
max_is_set++;
max = string_to_frequency(optarg);
if (max == 0) {
print_unknown_arg();
return -EINVAL;
}
break;
case 'f':
if (freq_is_set)
double_parm++;
freq_is_set++;
freq = string_to_frequency(optarg);
if (freq == 0) {
print_unknown_arg();
return -EINVAL;
}
break;
case 'g':
if (gov_is_set)
double_parm++;
gov_is_set++;
if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) {
print_unknown_arg();
return -EINVAL;
}
if ((sscanf(optarg, "%s", gov)) != 1) {
print_unknown_arg();
return -EINVAL;
}
break;
}
} while(cont);
if (double_parm) {
print_header();
printf("the same parameter was passed more than once\n");
return -EINVAL;
}
if (freq_is_set) {
if ((min_is_set) || (max_is_set) || (gov_is_set)) {
printf(gettext ("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n"
"-g/--governor parameters\n"));
return -EINVAL;
}
ret = cpufreq_set_frequency(cpu, freq);
goto out;
}
ret = min_is_set + max_is_set + gov_is_set;
if (!ret) {
printf(gettext ("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n"
"-g/--governor must be passed\n"));
return -EINVAL;
}
if (ret == 1) {
if (min_is_set)
ret = cpufreq_modify_policy_min(cpu, min);
else if (max_is_set)
ret = cpufreq_modify_policy_max(cpu, max);
else if (gov_is_set)
ret = cpufreq_modify_policy_governor(cpu, gov);
if (!ret)
return 0;
}
{
struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu);
struct cpufreq_policy new_pol;
if (!cur_pol) {
printf(gettext ("wrong, unknown or unhandled CPU?\n"));
return -EINVAL;
}
if (min_is_set)
new_pol.min = min;
else
new_pol.min = cur_pol->min;
if (max_is_set)
new_pol.max = max;
else
new_pol.max = cur_pol->max;
new_pol.governor = gov;
if (!gov_is_set)
strncpy(gov, cur_pol->governor, 20);
cpufreq_put_policy(cur_pol);
ret = cpufreq_set_policy(cpu, &new_pol);
}
out:
if (ret) {
printf(gettext ("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"));
}
return (ret);
}