2007-05-04 00:27:45 +04:00
/*
* Sysfs interface for the universal power supply monitor class
*
* Copyright © 2007 David Woodhouse < dwmw2 @ infradead . org >
* Copyright © 2007 Anton Vorontsov < cbou @ mail . ru >
* Copyright © 2004 Szabolcs Gyurko
* Copyright © 2003 Ian Molton < spyro @ f2s . com >
*
* Modified : 2004 , Oct Szabolcs Gyurko
*
* You may use this code as per GPL version 2
*/
# include <linux/ctype.h>
# include <linux/power_supply.h>
2007-11-26 00:25:45 +03:00
# include "power_supply.h"
2007-05-04 00:27:45 +04:00
/*
* This is because the name " current " breaks the device attr macro .
* The " current " word resolves to " (get_current()) " so instead of
* " current " " (get_current()) " appears in the sysfs .
*
* The source of this definition is the device . h which calls __ATTR
* macro in sysfs . h which calls the __stringify macro .
*
* Only modification that the name is not tried to be resolved
* ( as a macro let ' s say ) .
*/
# define POWER_SUPPLY_ATTR(_name) \
{ \
2008-10-19 07:28:50 +04:00
. attr = { . name = # _name , . mode = 0444 } , \
2007-05-04 00:27:45 +04:00
. show = power_supply_show_property , \
. store = NULL , \
}
static struct device_attribute power_supply_attrs [ ] ;
static ssize_t power_supply_show_property ( struct device * dev ,
struct device_attribute * attr ,
char * buf ) {
static char * status_text [ ] = {
" Unknown " , " Charging " , " Discharging " , " Not charging " , " Full "
} ;
2009-07-02 17:45:18 +04:00
static char * charge_type [ ] = {
" Unknown " , " N/A " , " Trickle " , " Fast "
} ;
2007-05-04 00:27:45 +04:00
static char * health_text [ ] = {
" Unknown " , " Good " , " Overheat " , " Dead " , " Over voltage " ,
2008-12-01 00:43:21 +03:00
" Unspecified failure " , " Cold " ,
2007-05-04 00:27:45 +04:00
} ;
static char * technology_text [ ] = {
2008-01-07 04:12:41 +03:00
" Unknown " , " NiMH " , " Li-ion " , " Li-poly " , " LiFe " , " NiCd " ,
" LiMn "
2007-05-04 00:27:45 +04:00
} ;
2009-06-30 10:13:01 +04:00
static char * capacity_level_text [ ] = {
" Unknown " , " Critical " , " Low " , " Normal " , " High " , " Full "
} ;
2007-05-04 00:27:45 +04:00
ssize_t ret ;
struct power_supply * psy = dev_get_drvdata ( dev ) ;
const ptrdiff_t off = attr - power_supply_attrs ;
union power_supply_propval value ;
ret = psy - > get_property ( psy , off , & value ) ;
if ( ret < 0 ) {
2009-12-03 00:24:51 +03:00
if ( ret = = - ENODATA )
dev_dbg ( dev , " driver has no data for `%s' property \n " ,
attr - > attr . name ) ;
else if ( ret ! = - ENODEV )
2007-05-04 00:27:45 +04:00
dev_err ( dev , " driver failed to report `%s' property \n " ,
attr - > attr . name ) ;
return ret ;
}
if ( off = = POWER_SUPPLY_PROP_STATUS )
return sprintf ( buf , " %s \n " , status_text [ value . intval ] ) ;
2009-07-02 17:45:18 +04:00
else if ( off = = POWER_SUPPLY_PROP_CHARGE_TYPE )
return sprintf ( buf , " %s \n " , charge_type [ value . intval ] ) ;
2007-05-04 00:27:45 +04:00
else if ( off = = POWER_SUPPLY_PROP_HEALTH )
return sprintf ( buf , " %s \n " , health_text [ value . intval ] ) ;
else if ( off = = POWER_SUPPLY_PROP_TECHNOLOGY )
return sprintf ( buf , " %s \n " , technology_text [ value . intval ] ) ;
2009-06-30 10:13:01 +04:00
else if ( off = = POWER_SUPPLY_PROP_CAPACITY_LEVEL )
return sprintf ( buf , " %s \n " , capacity_level_text [ value . intval ] ) ;
2007-05-04 00:27:45 +04:00
else if ( off > = POWER_SUPPLY_PROP_MODEL_NAME )
return sprintf ( buf , " %s \n " , value . strval ) ;
return sprintf ( buf , " %d \n " , value . intval ) ;
}
/* Must be in the same order as POWER_SUPPLY_PROP_* */
static struct device_attribute power_supply_attrs [ ] = {
/* Properties of type `int' */
POWER_SUPPLY_ATTR ( status ) ,
2009-07-02 17:45:18 +04:00
POWER_SUPPLY_ATTR ( charge_type ) ,
2007-05-04 00:27:45 +04:00
POWER_SUPPLY_ATTR ( health ) ,
POWER_SUPPLY_ATTR ( present ) ,
POWER_SUPPLY_ATTR ( online ) ,
POWER_SUPPLY_ATTR ( technology ) ,
2008-01-07 04:12:41 +03:00
POWER_SUPPLY_ATTR ( voltage_max ) ,
POWER_SUPPLY_ATTR ( voltage_min ) ,
2007-05-04 00:27:45 +04:00
POWER_SUPPLY_ATTR ( voltage_max_design ) ,
POWER_SUPPLY_ATTR ( voltage_min_design ) ,
POWER_SUPPLY_ATTR ( voltage_now ) ,
POWER_SUPPLY_ATTR ( voltage_avg ) ,
POWER_SUPPLY_ATTR ( current_now ) ,
POWER_SUPPLY_ATTR ( current_avg ) ,
2009-03-28 05:23:52 +03:00
POWER_SUPPLY_ATTR ( power_now ) ,
POWER_SUPPLY_ATTR ( power_avg ) ,
2007-05-04 00:27:45 +04:00
POWER_SUPPLY_ATTR ( charge_full_design ) ,
POWER_SUPPLY_ATTR ( charge_empty_design ) ,
POWER_SUPPLY_ATTR ( charge_full ) ,
POWER_SUPPLY_ATTR ( charge_empty ) ,
POWER_SUPPLY_ATTR ( charge_now ) ,
POWER_SUPPLY_ATTR ( charge_avg ) ,
2008-05-13 05:46:29 +04:00
POWER_SUPPLY_ATTR ( charge_counter ) ,
2007-05-04 00:27:45 +04:00
POWER_SUPPLY_ATTR ( energy_full_design ) ,
POWER_SUPPLY_ATTR ( energy_empty_design ) ,
POWER_SUPPLY_ATTR ( energy_full ) ,
POWER_SUPPLY_ATTR ( energy_empty ) ,
POWER_SUPPLY_ATTR ( energy_now ) ,
POWER_SUPPLY_ATTR ( energy_avg ) ,
POWER_SUPPLY_ATTR ( capacity ) ,
2009-06-30 10:13:01 +04:00
POWER_SUPPLY_ATTR ( capacity_level ) ,
2007-05-04 00:27:45 +04:00
POWER_SUPPLY_ATTR ( temp ) ,
POWER_SUPPLY_ATTR ( temp_ambient ) ,
POWER_SUPPLY_ATTR ( time_to_empty_now ) ,
POWER_SUPPLY_ATTR ( time_to_empty_avg ) ,
POWER_SUPPLY_ATTR ( time_to_full_now ) ,
POWER_SUPPLY_ATTR ( time_to_full_avg ) ,
/* Properties of type `const char *' */
POWER_SUPPLY_ATTR ( model_name ) ,
POWER_SUPPLY_ATTR ( manufacturer ) ,
2008-01-22 20:46:50 +03:00
POWER_SUPPLY_ATTR ( serial_number ) ,
2007-05-04 00:27:45 +04:00
} ;
static ssize_t power_supply_show_static_attrs ( struct device * dev ,
struct device_attribute * attr ,
char * buf ) {
static char * type_text [ ] = { " Battery " , " UPS " , " Mains " , " USB " } ;
struct power_supply * psy = dev_get_drvdata ( dev ) ;
return sprintf ( buf , " %s \n " , type_text [ psy - > type ] ) ;
}
static struct device_attribute power_supply_static_attrs [ ] = {
__ATTR ( type , 0444 , power_supply_show_static_attrs , NULL ) ,
} ;
int power_supply_create_attrs ( struct power_supply * psy )
{
int rc = 0 ;
int i , j ;
for ( i = 0 ; i < ARRAY_SIZE ( power_supply_static_attrs ) ; i + + ) {
rc = device_create_file ( psy - > dev ,
& power_supply_static_attrs [ i ] ) ;
if ( rc )
goto statics_failed ;
}
for ( j = 0 ; j < psy - > num_properties ; j + + ) {
rc = device_create_file ( psy - > dev ,
& power_supply_attrs [ psy - > properties [ j ] ] ) ;
if ( rc )
goto dynamics_failed ;
}
goto succeed ;
dynamics_failed :
while ( j - - )
device_remove_file ( psy - > dev ,
& power_supply_attrs [ psy - > properties [ j ] ] ) ;
statics_failed :
while ( i - - )
2007-12-12 22:12:59 +03:00
device_remove_file ( psy - > dev , & power_supply_static_attrs [ i ] ) ;
2007-05-04 00:27:45 +04:00
succeed :
return rc ;
}
void power_supply_remove_attrs ( struct power_supply * psy )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( power_supply_static_attrs ) ; i + + )
2007-12-12 22:12:59 +03:00
device_remove_file ( psy - > dev , & power_supply_static_attrs [ i ] ) ;
2007-05-04 00:27:45 +04:00
for ( i = 0 ; i < psy - > num_properties ; i + + )
device_remove_file ( psy - > dev ,
& power_supply_attrs [ psy - > properties [ i ] ] ) ;
}
static char * kstruprdup ( const char * str , gfp_t gfp )
{
char * ret , * ustr ;
ustr = ret = kmalloc ( strlen ( str ) + 1 , gfp ) ;
if ( ! ret )
return NULL ;
while ( * str )
* ustr + + = toupper ( * str + + ) ;
* ustr = 0 ;
return ret ;
}
2007-08-14 17:15:12 +04:00
int power_supply_uevent ( struct device * dev , struct kobj_uevent_env * env )
2007-05-04 00:27:45 +04:00
{
struct power_supply * psy = dev_get_drvdata ( dev ) ;
2007-08-14 17:15:12 +04:00
int ret = 0 , j ;
2007-05-04 00:27:45 +04:00
char * prop_buf ;
char * attrname ;
dev_dbg ( dev , " uevent \n " ) ;
2008-06-08 19:43:42 +04:00
if ( ! psy | | ! psy - > dev ) {
2007-05-04 00:27:45 +04:00
dev_dbg ( dev , " No power supply yet \n " ) ;
return ret ;
}
dev_dbg ( dev , " POWER_SUPPLY_NAME=%s \n " , psy - > name ) ;
2007-08-14 17:15:12 +04:00
ret = add_uevent_var ( env , " POWER_SUPPLY_NAME=%s " , psy - > name ) ;
2007-05-04 00:27:45 +04:00
if ( ret )
return ret ;
prop_buf = ( char * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! prop_buf )
return - ENOMEM ;
for ( j = 0 ; j < ARRAY_SIZE ( power_supply_static_attrs ) ; j + + ) {
struct device_attribute * attr ;
char * line ;
attr = & power_supply_static_attrs [ j ] ;
ret = power_supply_show_static_attrs ( dev , attr , prop_buf ) ;
if ( ret < 0 )
goto out ;
line = strchr ( prop_buf , ' \n ' ) ;
if ( line )
* line = 0 ;
attrname = kstruprdup ( attr - > attr . name , GFP_KERNEL ) ;
if ( ! attrname ) {
ret = - ENOMEM ;
goto out ;
}
dev_dbg ( dev , " Static prop %s=%s \n " , attrname , prop_buf ) ;
2007-08-14 17:15:12 +04:00
ret = add_uevent_var ( env , " POWER_SUPPLY_%s=%s " , attrname , prop_buf ) ;
2007-05-04 00:27:45 +04:00
kfree ( attrname ) ;
if ( ret )
goto out ;
}
dev_dbg ( dev , " %zd dynamic props \n " , psy - > num_properties ) ;
for ( j = 0 ; j < psy - > num_properties ; j + + ) {
struct device_attribute * attr ;
char * line ;
attr = & power_supply_attrs [ psy - > properties [ j ] ] ;
ret = power_supply_show_property ( dev , attr , prop_buf ) ;
if ( ret = = - ENODEV ) {
/* When a battery is absent, we expect -ENODEV. Don't abort;
send the uevent with at least the the PRESENT = 0 property */
ret = 0 ;
continue ;
}
if ( ret < 0 )
goto out ;
line = strchr ( prop_buf , ' \n ' ) ;
if ( line )
* line = 0 ;
attrname = kstruprdup ( attr - > attr . name , GFP_KERNEL ) ;
if ( ! attrname ) {
ret = - ENOMEM ;
goto out ;
}
dev_dbg ( dev , " prop %s=%s \n " , attrname , prop_buf ) ;
2007-08-14 17:15:12 +04:00
ret = add_uevent_var ( env , " POWER_SUPPLY_%s=%s " , attrname , prop_buf ) ;
2007-05-04 00:27:45 +04:00
kfree ( attrname ) ;
if ( ret )
goto out ;
}
out :
free_page ( ( unsigned long ) prop_buf ) ;
return ret ;
}