2011-03-30 16:30:11 +02:00
/*
* ( 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"
# define NORM_FREQ_LEN 32
static struct option set_opts [ ] = {
2011-04-19 20:33:50 +02:00
{ . 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 = " related " , . has_arg = no_argument , . flag = NULL , . val = ' r ' } ,
2011-03-30 16:30:11 +02:00
{ } ,
} ;
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 {
2011-04-19 20:33:50 +02:00
char * str_unit ;
2011-03-30 16:30:11 +02:00
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 )
2011-04-19 20:33:50 +02:00
return cpufreq_modify_policy_governor ( cpu ,
new_pol - > governor ) ;
2011-03-30 16:30:11 +02:00
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 {
2011-08-06 18:11:43 +02:00
ret = getopt_long ( argc , argv , " d:u:g:f:r " , set_opts , NULL ) ;
2011-03-30 16:30:11 +02:00
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 ;
2011-04-19 20:33:50 +02:00
}
2013-12-17 15:07:31 +00:00
if ( ( sscanf ( optarg , " %19s " , gov ) ) ! = 1 ) {
2011-03-30 16:30:11 +02:00
print_unknown_arg ( ) ;
return - EINVAL ;
2011-04-19 20:33:50 +02:00
}
2011-03-30 16:30:11 +02:00
new_pol . governor = gov ;
break ;
}
2011-04-19 20:33:50 +02:00
} while ( cont ) ;
2011-03-30 16:30:11 +02:00
/* 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 + + ) {
2011-04-19 20:33:50 +02:00
2011-03-30 16:30:11 +02:00
if ( ! bitmask_isbitset ( cpus_chosen , cpu ) | |
cpufreq_cpu_exists ( cpu ) )
continue ;
printf ( _ ( " Setting cpu: %d \n " ) , cpu ) ;
ret = do_one_cpu ( cpu , & new_pol , freq , policychange ) ;
2014-07-29 18:12:20 +02:00
if ( ret ) {
print_error ( ) ;
return ret ;
}
2011-03-30 16:30:11 +02:00
}
2014-07-29 18:12:20 +02:00
return 0 ;
2011-03-30 16:30:11 +02:00
}