1/* 2 * Universal power supply monitor class 3 * 4 * Copyright �� 2007 Anton Vorontsov <cbou@mail.ru> 5 * Copyright �� 2004 Szabolcs Gyurko 6 * Copyright �� 2003 Ian Molton <spyro@f2s.com> 7 * 8 * Modified: 2004, Oct Szabolcs Gyurko 9 * 10 * You may use this code as per GPL version 2 11 */ 12 13#include <linux/module.h> 14#include <linux/types.h> 15#include <linux/init.h> 16#include <linux/slab.h> 17#include <linux/device.h> 18#include <linux/err.h> 19#include <linux/power_supply.h> 20#include "power_supply.h" 21 22/* exported for the APM Power driver, APM emulation */ 23struct class *power_supply_class; 24EXPORT_SYMBOL_GPL(power_supply_class); 25 26static struct device_type power_supply_dev_type; 27 28static int __power_supply_changed_work(struct device *dev, void *data) 29{ 30 struct power_supply *psy = (struct power_supply *)data; 31 struct power_supply *pst = dev_get_drvdata(dev); 32 int i; 33 34 for (i = 0; i < psy->num_supplicants; i++) 35 if (!strcmp(psy->supplied_to[i], pst->name)) { 36 if (pst->external_power_changed) 37 pst->external_power_changed(pst); 38 } 39 return 0; 40} 41 42static void power_supply_changed_work(struct work_struct *work) 43{ 44 struct power_supply *psy = container_of(work, struct power_supply, 45 changed_work); 46 47 dev_dbg(psy->dev, "%s\n", __func__); 48 49 class_for_each_device(power_supply_class, NULL, psy, 50 __power_supply_changed_work); 51 52 power_supply_update_leds(psy); 53 54 kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); 55} 56 57void power_supply_changed(struct power_supply *psy) 58{ 59 dev_dbg(psy->dev, "%s\n", __func__); 60 61 schedule_work(&psy->changed_work); 62} 63EXPORT_SYMBOL_GPL(power_supply_changed); 64 65static int __power_supply_am_i_supplied(struct device *dev, void *data) 66{ 67 union power_supply_propval ret = {0,}; 68 struct power_supply *psy = (struct power_supply *)data; 69 struct power_supply *epsy = dev_get_drvdata(dev); 70 int i; 71 72 for (i = 0; i < epsy->num_supplicants; i++) { 73 if (!strcmp(epsy->supplied_to[i], psy->name)) { 74 if (epsy->get_property(epsy, 75 POWER_SUPPLY_PROP_ONLINE, &ret)) 76 continue; 77 if (ret.intval) 78 return ret.intval; 79 } 80 } 81 return 0; 82} 83 84int power_supply_am_i_supplied(struct power_supply *psy) 85{ 86 int error; 87 88 error = class_for_each_device(power_supply_class, NULL, psy, 89 __power_supply_am_i_supplied); 90 91 dev_dbg(psy->dev, "%s %d\n", __func__, error); 92 93 return error; 94} 95EXPORT_SYMBOL_GPL(power_supply_am_i_supplied); 96 97static int __power_supply_is_system_supplied(struct device *dev, void *data) 98{ 99 union power_supply_propval ret = {0,}; 100 struct power_supply *psy = dev_get_drvdata(dev); 101 102 if (psy->type != POWER_SUPPLY_TYPE_BATTERY) { 103 if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret)) 104 return 0; 105 if (ret.intval) 106 return ret.intval; 107 } 108 return 0; 109} 110 111int power_supply_is_system_supplied(void) 112{ 113 int error; 114 115 error = class_for_each_device(power_supply_class, NULL, NULL, 116 __power_supply_is_system_supplied); 117 118 return error; 119} 120EXPORT_SYMBOL_GPL(power_supply_is_system_supplied); 121 122int power_supply_set_battery_charged(struct power_supply *psy) 123{ 124 if (psy->type == POWER_SUPPLY_TYPE_BATTERY && psy->set_charged) { 125 psy->set_charged(psy); 126 return 0; 127 } 128 129 return -EINVAL; 130} 131EXPORT_SYMBOL_GPL(power_supply_set_battery_charged); 132 133static int power_supply_match_device_by_name(struct device *dev, void *data) 134{ 135 const char *name = data; 136 struct power_supply *psy = dev_get_drvdata(dev); 137 138 return strcmp(psy->name, name) == 0; 139} 140 141struct power_supply *power_supply_get_by_name(char *name) 142{ 143 struct device *dev = class_find_device(power_supply_class, NULL, name, 144 power_supply_match_device_by_name); 145 146 return dev ? dev_get_drvdata(dev) : NULL; 147} 148EXPORT_SYMBOL_GPL(power_supply_get_by_name); 149 150static void power_supply_dev_release(struct device *dev) 151{ 152 pr_debug("device: '%s': %s\n", dev_name(dev), __func__); 153 kfree(dev); 154} 155 156int power_supply_register(struct device *parent, struct power_supply *psy) 157{ 158 struct device *dev; 159 int rc; 160 161 dev = kzalloc(sizeof(*dev), GFP_KERNEL); 162 if (!dev) 163 return -ENOMEM; 164 165 device_initialize(dev); 166 167 dev->class = power_supply_class; 168 dev->type = &power_supply_dev_type; 169 dev->parent = parent; 170 dev->release = power_supply_dev_release; 171 dev_set_drvdata(dev, psy); 172 psy->dev = dev; 173 174 rc = kobject_set_name(&dev->kobj, "%s", psy->name); 175 if (rc) 176 goto kobject_set_name_failed; 177 178 rc = device_add(dev); 179 if (rc) 180 goto device_add_failed; 181 182 INIT_WORK(&psy->changed_work, power_supply_changed_work); 183 184 rc = power_supply_create_triggers(psy); 185 if (rc) 186 goto create_triggers_failed; 187 188 power_supply_changed(psy); 189 190 goto success; 191 192create_triggers_failed: 193 device_unregister(psy->dev); 194kobject_set_name_failed: 195device_add_failed: 196 kfree(dev); 197success: 198 return rc; 199} 200EXPORT_SYMBOL_GPL(power_supply_register); 201 202void power_supply_unregister(struct power_supply *psy) 203{ 204 flush_scheduled_work(); 205 power_supply_remove_triggers(psy); 206 device_unregister(psy->dev); 207} 208EXPORT_SYMBOL_GPL(power_supply_unregister); 209 210static int __init power_supply_class_init(void) 211{ 212 power_supply_class = class_create(THIS_MODULE, "power_supply"); 213 214 if (IS_ERR(power_supply_class)) 215 return PTR_ERR(power_supply_class); 216 217 power_supply_class->dev_uevent = power_supply_uevent; 218 power_supply_init_attrs(&power_supply_dev_type); 219 220 return 0; 221} 222 223static void __exit power_supply_class_exit(void) 224{ 225 class_destroy(power_supply_class); 226} 227 228subsys_initcall(power_supply_class_init); 229module_exit(power_supply_class_exit); 230 231MODULE_DESCRIPTION("Universal power supply monitor class"); 232MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, " 233 "Szabolcs Gyurko, " 234 "Anton Vorontsov <cbou@mail.ru>"); 235MODULE_LICENSE("GPL"); 236