2007-05-04 00:27:45 +04:00
/*
* Universal power supply monitor class
*
* 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/module.h>
# include <linux/types.h>
# include <linux/init.h>
2010-05-18 23:49:51 +04:00
# include <linux/slab.h>
2007-05-04 00:27:45 +04:00
# include <linux/device.h>
# include <linux/err.h>
# include <linux/power_supply.h>
# include "power_supply.h"
2009-07-30 17:42:31 +04:00
/* exported for the APM Power driver, APM emulation */
2007-05-04 00:27:45 +04:00
struct class * power_supply_class ;
2009-07-30 17:42:31 +04:00
EXPORT_SYMBOL_GPL ( power_supply_class ) ;
2007-05-04 00:27:45 +04:00
2010-05-18 23:49:51 +04:00
static struct device_type power_supply_dev_type ;
2008-01-22 08:58:22 +03:00
static int __power_supply_changed_work ( struct device * dev , void * data )
{
struct power_supply * psy = ( struct power_supply * ) data ;
struct power_supply * pst = dev_get_drvdata ( dev ) ;
int i ;
for ( i = 0 ; i < psy - > num_supplicants ; i + + )
if ( ! strcmp ( psy - > supplied_to [ i ] , pst - > name ) ) {
if ( pst - > external_power_changed )
pst - > external_power_changed ( pst ) ;
}
return 0 ;
}
2007-05-04 00:27:45 +04:00
static void power_supply_changed_work ( struct work_struct * work )
{
struct power_supply * psy = container_of ( work , struct power_supply ,
changed_work ) ;
2008-04-29 11:58:29 +04:00
dev_dbg ( psy - > dev , " %s \n " , __func__ ) ;
2007-05-04 00:27:45 +04:00
2008-05-23 01:21:08 +04:00
class_for_each_device ( power_supply_class , NULL , psy ,
2008-01-22 08:58:22 +03:00
__power_supply_changed_work ) ;
2007-05-04 00:27:45 +04:00
power_supply_update_leds ( psy ) ;
kobject_uevent ( & psy - > dev - > kobj , KOBJ_CHANGE ) ;
}
void power_supply_changed ( struct power_supply * psy )
{
2008-04-29 11:58:29 +04:00
dev_dbg ( psy - > dev , " %s \n " , __func__ ) ;
2007-05-04 00:27:45 +04:00
schedule_work ( & psy - > changed_work ) ;
}
2009-07-30 17:42:31 +04:00
EXPORT_SYMBOL_GPL ( power_supply_changed ) ;
2007-05-04 00:27:45 +04:00
2008-01-22 08:58:22 +03:00
static int __power_supply_am_i_supplied ( struct device * dev , void * data )
2007-05-04 00:27:45 +04:00
{
union power_supply_propval ret = { 0 , } ;
2008-01-22 08:58:22 +03:00
struct power_supply * psy = ( struct power_supply * ) data ;
struct power_supply * epsy = dev_get_drvdata ( dev ) ;
int i ;
for ( i = 0 ; i < epsy - > num_supplicants ; i + + ) {
if ( ! strcmp ( epsy - > supplied_to [ i ] , psy - > name ) ) {
if ( epsy - > get_property ( epsy ,
POWER_SUPPLY_PROP_ONLINE , & ret ) )
continue ;
if ( ret . intval )
return ret . intval ;
2007-05-04 00:27:45 +04:00
}
}
2008-01-22 08:58:22 +03:00
return 0 ;
}
int power_supply_am_i_supplied ( struct power_supply * psy )
{
int error ;
2008-05-23 01:21:08 +04:00
error = class_for_each_device ( power_supply_class , NULL , psy ,
2008-01-22 08:58:22 +03:00
__power_supply_am_i_supplied ) ;
2007-05-04 00:27:45 +04:00
2008-04-29 11:58:29 +04:00
dev_dbg ( psy - > dev , " %s %d \n " , __func__ , error ) ;
2007-05-04 00:27:45 +04:00
2008-01-22 08:58:22 +03:00
return error ;
2007-05-04 00:27:45 +04:00
}
2009-07-30 17:42:31 +04:00
EXPORT_SYMBOL_GPL ( power_supply_am_i_supplied ) ;
2007-05-04 00:27:45 +04:00
2008-08-27 00:09:59 +04:00
static int __power_supply_is_system_supplied ( struct device * dev , void * data )
{
union power_supply_propval ret = { 0 , } ;
struct power_supply * psy = dev_get_drvdata ( dev ) ;
2011-12-11 01:53:36 +04:00
unsigned int * count = data ;
2008-08-27 00:09:59 +04:00
2011-12-11 01:53:36 +04:00
( * count ) + + ;
2008-08-27 00:09:59 +04:00
if ( psy - > type ! = POWER_SUPPLY_TYPE_BATTERY ) {
if ( psy - > get_property ( psy , POWER_SUPPLY_PROP_ONLINE , & ret ) )
return 0 ;
if ( ret . intval )
return ret . intval ;
}
return 0 ;
}
int power_supply_is_system_supplied ( void )
{
int error ;
2011-12-11 01:53:36 +04:00
unsigned int count = 0 ;
2008-08-27 00:09:59 +04:00
2011-12-11 01:53:36 +04:00
error = class_for_each_device ( power_supply_class , NULL , & count ,
2008-08-27 00:09:59 +04:00
__power_supply_is_system_supplied ) ;
2011-12-11 01:53:36 +04:00
/*
* If no power class device was found at all , most probably we are
* running on a desktop system , so assume we are on mains power .
*/
if ( count = = 0 )
return 1 ;
2008-08-27 00:09:59 +04:00
return error ;
}
2009-07-30 17:42:31 +04:00
EXPORT_SYMBOL_GPL ( power_supply_is_system_supplied ) ;
2008-08-27 00:09:59 +04:00
2009-07-23 22:35:53 +04:00
int power_supply_set_battery_charged ( struct power_supply * psy )
{
if ( psy - > type = = POWER_SUPPLY_TYPE_BATTERY & & psy - > set_charged ) {
psy - > set_charged ( psy ) ;
return 0 ;
}
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( power_supply_set_battery_charged ) ;
static int power_supply_match_device_by_name ( struct device * dev , void * data )
{
const char * name = data ;
struct power_supply * psy = dev_get_drvdata ( dev ) ;
return strcmp ( psy - > name , name ) = = 0 ;
}
struct power_supply * power_supply_get_by_name ( char * name )
{
struct device * dev = class_find_device ( power_supply_class , NULL , name ,
power_supply_match_device_by_name ) ;
return dev ? dev_get_drvdata ( dev ) : NULL ;
}
EXPORT_SYMBOL_GPL ( power_supply_get_by_name ) ;
2011-12-07 21:15:45 +04:00
int power_supply_powers ( struct power_supply * psy , struct device * dev )
{
2012-01-05 19:17:25 +04:00
return sysfs_create_link ( & psy - > dev - > kobj , & dev - > kobj , " powers " ) ;
2011-12-07 21:15:45 +04:00
}
EXPORT_SYMBOL_GPL ( power_supply_powers ) ;
2010-05-18 23:49:51 +04:00
static void power_supply_dev_release ( struct device * dev )
{
pr_debug ( " device: '%s': %s \n " , dev_name ( dev ) , __func__ ) ;
kfree ( dev ) ;
}
2007-05-04 00:27:45 +04:00
int power_supply_register ( struct device * parent , struct power_supply * psy )
{
2010-05-18 23:49:51 +04:00
struct device * dev ;
int rc ;
2007-05-04 00:27:45 +04:00
2010-05-18 23:49:51 +04:00
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev )
return - ENOMEM ;
2007-05-04 00:27:45 +04:00
2010-05-18 23:49:51 +04:00
device_initialize ( dev ) ;
2007-05-04 00:27:45 +04:00
2010-05-18 23:49:51 +04:00
dev - > class = power_supply_class ;
dev - > type = & power_supply_dev_type ;
dev - > parent = parent ;
dev - > release = power_supply_dev_release ;
dev_set_drvdata ( dev , psy ) ;
psy - > dev = dev ;
2011-02-21 17:34:19 +03:00
INIT_WORK ( & psy - > changed_work , power_supply_changed_work ) ;
2010-05-18 23:49:51 +04:00
rc = kobject_set_name ( & dev - > kobj , " %s " , psy - > name ) ;
if ( rc )
goto kobject_set_name_failed ;
rc = device_add ( dev ) ;
2007-05-04 00:27:45 +04:00
if ( rc )
2010-05-18 23:49:51 +04:00
goto device_add_failed ;
2007-05-04 00:27:45 +04:00
rc = power_supply_create_triggers ( psy ) ;
if ( rc )
goto create_triggers_failed ;
power_supply_changed ( psy ) ;
goto success ;
create_triggers_failed :
2010-11-19 21:41:58 +03:00
device_del ( dev ) ;
2010-05-18 23:49:51 +04:00
kobject_set_name_failed :
device_add_failed :
2010-11-19 21:41:58 +03:00
put_device ( dev ) ;
2007-05-04 00:27:45 +04:00
success :
return rc ;
}
2009-07-30 17:42:31 +04:00
EXPORT_SYMBOL_GPL ( power_supply_register ) ;
2007-05-04 00:27:45 +04:00
void power_supply_unregister ( struct power_supply * psy )
{
2010-12-11 19:51:45 +03:00
cancel_work_sync ( & psy - > changed_work ) ;
2011-12-07 21:15:45 +04:00
sysfs_remove_link ( & psy - > dev - > kobj , " powers " ) ;
2007-05-04 00:27:45 +04:00
power_supply_remove_triggers ( psy ) ;
device_unregister ( psy - > dev ) ;
}
2009-07-30 17:42:31 +04:00
EXPORT_SYMBOL_GPL ( power_supply_unregister ) ;
2007-05-04 00:27:45 +04:00
static int __init power_supply_class_init ( void )
{
power_supply_class = class_create ( THIS_MODULE , " power_supply " ) ;
if ( IS_ERR ( power_supply_class ) )
return PTR_ERR ( power_supply_class ) ;
power_supply_class - > dev_uevent = power_supply_uevent ;
2010-05-18 23:49:51 +04:00
power_supply_init_attrs ( & power_supply_dev_type ) ;
2007-05-04 00:27:45 +04:00
return 0 ;
}
static void __exit power_supply_class_exit ( void )
{
class_destroy ( power_supply_class ) ;
}
subsys_initcall ( power_supply_class_init ) ;
module_exit ( power_supply_class_exit ) ;
MODULE_DESCRIPTION ( " Universal power supply monitor class " ) ;
MODULE_AUTHOR ( " Ian Molton <spyro@f2s.com>, "
" Szabolcs Gyurko, "
" Anton Vorontsov <cbou@mail.ru> " ) ;
MODULE_LICENSE ( " GPL " ) ;