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