power_supply: Add API for safe access of power supply function attrs
[firefly-linux-kernel-4.4.55.git] / drivers / power / power_supply_core.c
index a21e36ed8d414b60983568217dfee0b4806ad6a4..583dece8845b943476949fc0e56326a1aae22290 100644 (file)
@@ -314,7 +314,9 @@ EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
 
 int power_supply_set_battery_charged(struct power_supply *psy)
 {
-       if (psy->type == POWER_SUPPLY_TYPE_BATTERY && psy->set_charged) {
+       if (atomic_read(&psy->use_cnt) >= 0 &&
+                       psy->type == POWER_SUPPLY_TYPE_BATTERY &&
+                       psy->set_charged) {
                psy->set_charged(psy);
                return 0;
        }
@@ -366,6 +368,47 @@ struct power_supply *power_supply_get_by_phandle(struct device_node *np,
 EXPORT_SYMBOL_GPL(power_supply_get_by_phandle);
 #endif /* CONFIG_OF */
 
+int power_supply_get_property(struct power_supply *psy,
+                           enum power_supply_property psp,
+                           union power_supply_propval *val)
+{
+       if (atomic_read(&psy->use_cnt) <= 0)
+               return -ENODEV;
+
+       return psy->get_property(psy, psp, val);
+}
+EXPORT_SYMBOL_GPL(power_supply_get_property);
+
+int power_supply_set_property(struct power_supply *psy,
+                           enum power_supply_property psp,
+                           const union power_supply_propval *val)
+{
+       if (atomic_read(&psy->use_cnt) <= 0 || !psy->set_property)
+               return -ENODEV;
+
+       return psy->set_property(psy, psp, val);
+}
+EXPORT_SYMBOL_GPL(power_supply_set_property);
+
+int power_supply_property_is_writeable(struct power_supply *psy,
+                                       enum power_supply_property psp)
+{
+       if (atomic_read(&psy->use_cnt) <= 0 || !psy->property_is_writeable)
+               return -ENODEV;
+
+       return psy->property_is_writeable(psy, psp);
+}
+EXPORT_SYMBOL_GPL(power_supply_property_is_writeable);
+
+void power_supply_external_power_changed(struct power_supply *psy)
+{
+       if (atomic_read(&psy->use_cnt) <= 0 || !psy->external_power_changed)
+               return;
+
+       psy->external_power_changed(psy);
+}
+EXPORT_SYMBOL_GPL(power_supply_external_power_changed);
+
 int power_supply_powers(struct power_supply *psy, struct device *dev)
 {
        return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers");
@@ -555,6 +598,7 @@ static int __power_supply_register(struct device *parent,
        dev->release = power_supply_dev_release;
        dev_set_drvdata(dev, psy);
        psy->dev = dev;
+       atomic_inc(&psy->use_cnt);
        if (cfg) {
                psy->drv_data = cfg->drv_data;
                psy->of_node = cfg->of_node;
@@ -676,6 +720,7 @@ EXPORT_SYMBOL_GPL(devm_power_supply_register_no_ws);
 
 void power_supply_unregister(struct power_supply *psy)
 {
+       WARN_ON(atomic_dec_return(&psy->use_cnt));
        cancel_work_sync(&psy->changed_work);
        sysfs_remove_link(&psy->dev->kobj, "powers");
        power_supply_remove_triggers(psy);