| 
									
										
										
										
											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>
 | 
					
						
							|  |  |  | #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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-22 13:58:22 +08: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 00:58:29 -07:00
										 |  |  | 	dev_dbg(psy->dev, "%s\n", __func__); | 
					
						
							| 
									
										
										
										
											2007-05-04 00:27:45 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-22 17:21:08 -04:00
										 |  |  | 	class_for_each_device(power_supply_class, NULL, psy, | 
					
						
							| 
									
										
										
										
											2008-01-22 13:58:22 +08: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 00:58:29 -07: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 13:58:22 +08: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 13:58:22 +08: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 13:58:22 +08:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int power_supply_am_i_supplied(struct power_supply *psy) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int error; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-22 17:21:08 -04:00
										 |  |  | 	error = class_for_each_device(power_supply_class, NULL, psy, | 
					
						
							| 
									
										
										
										
											2008-01-22 13:58:22 +08:00
										 |  |  | 				      __power_supply_am_i_supplied); | 
					
						
							| 
									
										
										
										
											2007-05-04 00:27:45 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-29 00:58:29 -07:00
										 |  |  | 	dev_dbg(psy->dev, "%s %d\n", __func__, error); | 
					
						
							| 
									
										
										
										
											2007-05-04 00:27:45 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-22 13:58:22 +08: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-26 21:09:59 +01: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); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	error = class_for_each_device(power_supply_class, NULL, NULL, | 
					
						
							|  |  |  | 				      __power_supply_is_system_supplied); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return error; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-07-30 17:42:31 +04:00
										 |  |  | EXPORT_SYMBOL_GPL(power_supply_is_system_supplied); | 
					
						
							| 
									
										
										
										
											2008-08-26 21:09:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-23 20:35:53 +02: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); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-05-04 00:27:45 +04:00
										 |  |  | int power_supply_register(struct device *parent, struct power_supply *psy) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int rc = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-21 20:03:34 -07:00
										 |  |  | 	psy->dev = device_create(power_supply_class, parent, 0, psy, | 
					
						
							|  |  |  | 				 "%s", psy->name); | 
					
						
							| 
									
										
										
										
											2007-05-04 00:27:45 +04:00
										 |  |  | 	if (IS_ERR(psy->dev)) { | 
					
						
							|  |  |  | 		rc = PTR_ERR(psy->dev); | 
					
						
							|  |  |  | 		goto dev_create_failed; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	INIT_WORK(&psy->changed_work, power_supply_changed_work); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rc = power_supply_create_attrs(psy); | 
					
						
							|  |  |  | 	if (rc) | 
					
						
							|  |  |  | 		goto create_attrs_failed; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rc = power_supply_create_triggers(psy); | 
					
						
							|  |  |  | 	if (rc) | 
					
						
							|  |  |  | 		goto create_triggers_failed; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	power_supply_changed(psy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	goto success; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | create_triggers_failed: | 
					
						
							|  |  |  | 	power_supply_remove_attrs(psy); | 
					
						
							|  |  |  | create_attrs_failed: | 
					
						
							|  |  |  | 	device_unregister(psy->dev); | 
					
						
							|  |  |  | dev_create_failed: | 
					
						
							|  |  |  | 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) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	flush_scheduled_work(); | 
					
						
							|  |  |  | 	power_supply_remove_triggers(psy); | 
					
						
							|  |  |  | 	power_supply_remove_attrs(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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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"); |