public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/7] Battery class, external power framework, ds2760 battery
@ 2007-04-25 15:48 Anton Vorontsov
  2007-04-25 15:50 ` [PATCH 1/7] External power framework Anton Vorontsov
                   ` (7 more replies)
  0 siblings, 8 replies; 13+ messages in thread
From: Anton Vorontsov @ 2007-04-25 15:48 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, kernel-discuss

Hi all,

I believe most battery issues elaborated and settled, and battery
class has grown up for the -mm merge. Also, I hope that current
battery stuff will make everyone happy at some grade.

This patchset accumulated ideas and suggestions from reviews by
David Brownell, Evgeniy Polyakov, Greg KH, Henrique de Moraes Holschuh,
Ian Molton, Matt Reimer, Matthew Garrett, Ondrej Zajicek,
Paul Sokolovsky, Pavel Machek, Randy Dunlap, Russell King and
Shem Multinymous.

Thanks for the time you spent reviewing these patches!

On Tue, Apr 24, 2007 at 10:41:08PM +0200, Pavel Machek wrote:
> > > > Signed-off-by: Anton Vorontsov <cbou@mail.ru>
> > >
> > > Yes please. Generic battery support is badly needed.
> >
> > I'm glad you like it, thanks for the review!
> >
> > Also I've done some code split. It removes all #ifdefs from battery.c,
> > and also separates core from sysfs and leds.
> >
> > If there will no objections, I'll send this version when patch window
> > will opened.
>
> Actually, you probably want to add some changelogs, and send it to
> akpm _now_. He'll merge it to -mm tree, and hopefully send it upstream
> in the next window. Merging directly to linus is for bigger projects
> with git trees etc.

Andrew, would you please merge these patches into -mm? They're against
today's Linus git tree.

I hope it isn't too late for the merge. If it is, please tell me when I
should come back.

References:
http://lkml.org/lkml/2007/4/11/392
http://lkml.org/lkml/2007/4/11/393
http://lkml.org/lkml/2007/4/11/397
http://lkml.org/lkml/2007/4/11/398
http://lkml.org/lkml/2007/4/20/154

Thanks,

-- 
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.org/bd2

^ permalink raw reply	[flat|nested] 13+ messages in thread
* [PATCH 1/7] External power framework
@ 2007-04-20 11:57 Anton Vorontsov
  0 siblings, 0 replies; 13+ messages in thread
From: Anton Vorontsov @ 2007-04-20 11:57 UTC (permalink / raw)
  To: linux-kernel; +Cc: kernel-discuss

External power framework - power supplies and power supplicants.

Supplicants (batteries so far) may ask to notify they when power supply
arrive/gone. This framework used by battery class (next patches).

It's permitted for supply to be bound to several supplicants (think main
and backup batteries).

It's also permitted for supplicants to consume power from several
external supplies (say AC and USB).

Here is how it look like from userspace:

        # pwd
        /sys/class/power_supply
        # ls
        ac  usb
        # cat ac/online usb/online
        1
        0


Signed-off-by: Anton Vorontsov <cbou@mail.ru>
---
 drivers/Kconfig                |    2 +
 drivers/Makefile               |    1 +
 drivers/power/Kconfig          |   13 ++
 drivers/power/Makefile         |    1 +
 drivers/power/external_power.c |  354 ++++++++++++++++++++++++++++++++++++++++
 include/linux/external_power.h |   56 +++++++
 6 files changed, 427 insertions(+), 0 deletions(-)
 create mode 100644 drivers/power/Kconfig
 create mode 100644 drivers/power/Makefile
 create mode 100644 drivers/power/external_power.c
 create mode 100644 include/linux/external_power.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 050323f..c546de3 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -54,6 +54,8 @@ source "drivers/spi/Kconfig"
 
 source "drivers/w1/Kconfig"
 
+source "drivers/power/Kconfig"
+
 source "drivers/hwmon/Kconfig"
 
 source "drivers/mfd/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 3a718f5..2bdaae7 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_I2O)		+= message/
 obj-$(CONFIG_RTC_LIB)		+= rtc/
 obj-$(CONFIG_I2C)		+= i2c/
 obj-$(CONFIG_W1)		+= w1/
+obj-$(CONFIG_EXTERNAL_POWER)	+= power/
 obj-$(CONFIG_HWMON)		+= hwmon/
 obj-$(CONFIG_PHONE)		+= telephony/
 obj-$(CONFIG_MD)		+= md/
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
new file mode 100644
index 0000000..17349c1
--- /dev/null
+++ b/drivers/power/Kconfig
@@ -0,0 +1,13 @@
+
+menu "External power support"
+
+config EXTERNAL_POWER
+	tristate "External power kernel interface"
+	help
+	  Say Y here to enable kernel external power detection interface,
+	  like AC or USB. Information also will exported to userspace via
+	  /sys/class/external_power/ directory.
+
+	  This interface is mandatory for battery class support.
+
+endmenu
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
new file mode 100644
index 0000000..c303b45
--- /dev/null
+++ b/drivers/power/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_EXTERNAL_POWER)  += external_power.o
diff --git a/drivers/power/external_power.c b/drivers/power/external_power.c
new file mode 100644
index 0000000..8960681
--- /dev/null
+++ b/drivers/power/external_power.c
@@ -0,0 +1,354 @@
+/*
+ * Linux kernel interface for external power suppliers/supplicants
+ *
+ * Copyright (c) 2007  Anton Vorontsov <cbou@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/rwsem.h>
+#include <linux/external_power.h>
+
+static struct class *power_supply_class;
+
+static LIST_HEAD(supplicants);
+static struct rw_semaphore supplicants_sem;
+
+struct bound_supply {
+	struct power_supply *psy;
+	struct list_head node;
+};
+
+struct bound_supplicant {
+	struct power_supplicant *pst;
+	struct list_head node;
+};
+
+int power_supplicant_am_i_supplied(struct power_supplicant *pst)
+{
+	int ret = 0;
+	struct bound_supply *bpsy;
+
+	pr_debug("%s\n", __FUNCTION__);
+	down(&power_supply_class->sem);
+	list_for_each_entry(bpsy, &pst->bound_supplies, node) {
+		if (bpsy->psy->is_online(bpsy->psy)) {
+			ret = 1;
+			goto out;
+		}
+	}
+out:
+	up(&power_supply_class->sem);
+
+	return ret;
+}
+
+static void unbind_pst_from_psys(struct power_supplicant *pst)
+{
+	struct bound_supply *bpsy, *bpsy_tmp;
+	struct bound_supplicant *bpst, *bpst_tmp;
+
+	list_for_each_entry_safe(bpsy, bpsy_tmp, &pst->bound_supplies, node) {
+		unsigned long flags;
+
+		write_lock_irqsave(&bpsy->psy->bpst_lock, flags);
+		list_for_each_entry_safe(bpst, bpst_tmp,
+		                &bpsy->psy->bound_supplicants, node) {
+			if (bpst->pst == pst) {
+				list_del(&bpst->node);
+				kfree(bpst);
+				break;
+			}
+		}
+		write_unlock_irqrestore(&bpsy->psy->bpst_lock, flags);
+
+		list_del(&bpsy->node);
+		kfree(bpsy);
+	}
+
+	return;
+}
+
+static void unbind_psy_from_psts(struct power_supply *psy)
+{
+	struct bound_supply *bpsy, *bpsy_tmp;
+	struct bound_supplicant *bpst, *bpst_tmp;
+	unsigned long flags;
+
+	write_lock_irqsave(&psy->bpst_lock, flags);
+	list_for_each_entry_safe(bpst, bpst_tmp, &psy->bound_supplicants,
+	                                                              node) {
+		list_for_each_entry_safe(bpsy, bpsy_tmp,
+		                &bpst->pst->bound_supplies, node) {
+			if (bpsy->psy == psy) {
+				list_del(&bpsy->node);
+				kfree(bpsy);
+				break;
+			}
+		}
+		list_del(&bpst->node);
+		kfree(bpst);
+	}
+	write_unlock_irqrestore(&psy->bpst_lock, flags);
+
+	return;
+}
+
+static int bind_pst_to_psy(struct power_supplicant *pst,
+                           struct power_supply *psy)
+{
+	struct bound_supplicant *bpst = kmalloc(sizeof(*bpst), GFP_KERNEL);
+	unsigned long flags;
+
+	if (!bpst)
+		return -ENOMEM;
+
+	bpst->pst = pst;
+
+	write_lock_irqsave(&psy->bpst_lock, flags);
+	list_add_tail(&bpst->node, &psy->bound_supplicants);
+	write_unlock_irqrestore(&psy->bpst_lock, flags);
+
+	pr_debug("power: bound pst %s to psy %s\n", pst->name, psy->name);
+
+	return 0;
+}
+
+static int bind_psy_to_pst(struct power_supply *psy,
+                           struct power_supplicant *pst)
+{
+	struct bound_supply *bpsy = kmalloc(sizeof(*bpsy), GFP_KERNEL);
+
+	if (!bpsy)
+		return -ENOMEM;
+
+	bpsy->psy = psy;
+	list_add_tail(&bpsy->node, &pst->bound_supplies);
+
+	pr_debug("power: bound psy %s to pst %s\n", psy->name, pst->name);
+
+	return 0;
+}
+
+int power_supplicant_register(struct power_supplicant *pst)
+{
+	int ret = 0;
+	size_t i;
+	struct device *dev;
+	struct power_supply *psy;
+
+	INIT_LIST_HEAD(&pst->bound_supplies);
+
+	down_write(&supplicants_sem);
+	down(&power_supply_class->sem);
+
+	list_for_each_entry(dev, &power_supply_class->devices, node) {
+		psy = dev_get_drvdata(dev);
+		for (i = 0; i < psy->num_supplicants; i++) {
+			if (!strcmp(pst->name, psy->supplied_to[i])) {
+				ret = bind_pst_to_psy(pst, psy);
+				if (ret)
+					goto binding_failed;
+				ret = bind_psy_to_pst(psy, pst);
+				if (ret)
+					goto binding_failed;
+				break;
+			}
+		}
+	}
+
+	list_add_tail(&pst->node, &supplicants);
+
+	goto succeed;
+
+binding_failed:
+	unbind_pst_from_psys(pst);
+succeed:
+	up(&power_supply_class->sem);
+	up_write(&supplicants_sem);
+
+	return ret;
+}
+
+void power_supplicant_unregister(struct power_supplicant *pst)
+{
+	down_write(&supplicants_sem);
+	list_del(&pst->node);
+	up_write(&supplicants_sem);
+
+	down(&power_supply_class->sem);
+	unbind_pst_from_psys(pst);
+	up(&power_supply_class->sem);
+	return;
+}
+
+void power_supply_changed(struct power_supply *psy)
+{
+	struct bound_supplicant *bpst;
+	unsigned long flags;
+
+	pr_debug("%s\n", __FUNCTION__);
+
+	read_lock_irqsave(&psy->bpst_lock, flags);
+	list_for_each_entry(bpst, &psy->bound_supplicants, node)
+		bpst->pst->power_supply_changed(bpst->pst, psy);
+	read_unlock_irqrestore(&psy->bpst_lock, flags);
+
+	return;
+}
+
+static ssize_t power_supply_show_online(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", psy->is_online(psy));
+}
+
+static ssize_t power_supply_show_type(struct device *dev,
+                                      struct device_attribute *attr, char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	return sprintf(buf, "%s\n", psy->type ? psy->type : "unknown");
+}
+
+static ssize_t power_supply_show_nominal_voltage(struct device *dev,
+                                                 struct device_attribute *attr,
+                                                 char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", psy->nominal_voltage);
+}
+
+static DEVICE_ATTR(online, 0444, power_supply_show_online, NULL);
+static DEVICE_ATTR(type, 0444, power_supply_show_type, NULL);
+static DEVICE_ATTR(nominal_voltage, 0444, power_supply_show_nominal_voltage,
+                   NULL);
+
+int power_supply_register(struct device *parent,
+                            struct power_supply *psy)
+{
+	int ret = 0;
+	struct power_supplicant *pst;
+	size_t i;
+
+	INIT_LIST_HEAD(&psy->bound_supplicants);
+	rwlock_init(&psy->bpst_lock);
+
+	psy->dev = device_create(power_supply_class, parent, 0, "%s",
+	                         psy->name);
+	if (IS_ERR(psy->dev)) {
+		ret = PTR_ERR(psy->dev);
+		goto dev_create_failed;
+	}
+
+	dev_set_drvdata(psy->dev, psy);
+
+	ret = device_create_file(psy->dev, &dev_attr_online);
+	if (ret)
+		goto create_online_failed;
+
+	ret = device_create_file(psy->dev, &dev_attr_type);
+	if (ret)
+		goto create_type_failed;
+
+	ret = device_create_file(psy->dev, &dev_attr_nominal_voltage);
+	if (ret)
+		goto create_nominal_voltage_failed;
+
+	down_write(&supplicants_sem);
+	list_for_each_entry(pst, &supplicants, node) {
+		for (i = 0; i < psy->num_supplicants; i++) {
+			if (!strcmp(pst->name, psy->supplied_to[i])) {
+				ret = bind_psy_to_pst(psy, pst);
+				if (ret)
+					goto binding_failed;
+				ret = bind_pst_to_psy(pst, psy);
+				if (ret)
+					goto binding_failed;
+				break;
+			}
+		}
+	}
+	up_write(&supplicants_sem);
+
+	/* notify supplicants that supply registred */
+	power_supply_changed(psy);
+
+	goto success;
+
+binding_failed:
+	unbind_psy_from_psts(psy);
+	device_remove_file(psy->dev, &dev_attr_nominal_voltage);
+create_nominal_voltage_failed:
+	device_remove_file(psy->dev, &dev_attr_type);
+create_type_failed:
+	device_remove_file(psy->dev, &dev_attr_online);
+create_online_failed:
+	device_unregister(psy->dev);
+dev_create_failed:
+success:
+	return ret;
+}
+
+void power_supply_unregister(struct power_supply *psy)
+{
+	down_write(&supplicants_sem);
+	unbind_psy_from_psts(psy);
+	up_write(&supplicants_sem);
+
+	device_remove_file(psy->dev, &dev_attr_nominal_voltage);
+	device_remove_file(psy->dev, &dev_attr_type);
+	device_remove_file(psy->dev, &dev_attr_online);
+
+	device_unregister(psy->dev);
+
+	return;
+}
+
+static int __init external_power_init(void)
+{
+	int ret = 0;
+
+	power_supply_class = class_create(THIS_MODULE, "power_supply");
+	if (IS_ERR(power_supply_class)) {
+		printk(KERN_ERR "external_power: failed to create "
+		       "power_supply class\n");
+		ret = PTR_ERR(power_supply_class);
+		goto class_create_failed;
+	}
+
+	init_rwsem(&supplicants_sem);
+
+	goto success;
+
+class_create_failed:
+success:
+	return ret;
+}
+
+static void __exit external_power_exit(void)
+{
+	class_destroy(power_supply_class);
+	return;
+}
+
+EXPORT_SYMBOL_GPL(power_supplicant_am_i_supplied);
+EXPORT_SYMBOL_GPL(power_supplicant_register);
+EXPORT_SYMBOL_GPL(power_supplicant_unregister);
+
+EXPORT_SYMBOL_GPL(power_supply_changed);
+EXPORT_SYMBOL_GPL(power_supply_register);
+EXPORT_SYMBOL_GPL(power_supply_unregister);
+
+subsys_initcall(external_power_init);
+module_exit(external_power_exit);
+
+MODULE_DESCRIPTION("Linux kernel interface for external power");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
diff --git a/include/linux/external_power.h b/include/linux/external_power.h
new file mode 100644
index 0000000..b635ff0
--- /dev/null
+++ b/include/linux/external_power.h
@@ -0,0 +1,56 @@
+/*
+ * Linux kernel interface for external power suppliers/supplicants
+ *
+ * Copyright (c) 2007  Anton Vorontsov <cbou@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __EXTERNAL_POWER_H__
+#define __EXTERNAL_POWER_H__
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+/* kernel interface for external power suppliers, like AC or USB */
+
+struct power_supply {
+	char *name;
+	char *type;
+	int nominal_voltage; /* units of uV */
+	int (*is_online)(struct power_supply *psy);
+	char **supplied_to;
+	size_t num_supplicants;
+
+	/* private */
+	struct list_head bound_supplicants;
+	rwlock_t bpst_lock;
+	struct device *dev;
+};
+
+extern void power_supply_changed(struct power_supply *psy);
+extern int power_supply_register(struct device *parent,
+                                 struct power_supply *psy);
+extern void power_supply_unregister(struct power_supply *psy);
+
+/* kernel interface for external power supplicants (batteries so far) */
+
+struct power_supplicant {
+	char *name;
+	/* used to notify supplicant about external power arrival/outage,
+	 * do not sleep there, this is called from irq, most probably */
+	void (*power_supply_changed)(struct power_supplicant *pst,
+	                             struct power_supply *psy);
+
+	/* private */
+	struct list_head bound_supplies;
+	struct list_head node;
+};
+
+extern int power_supplicant_am_i_supplied(struct power_supplicant *pst);
+extern int power_supplicant_register(struct power_supplicant *pst);
+extern void power_supplicant_unregister(struct power_supplicant *pst);
+
+#endif /* __EXTERNAL_POWER_H__ */
-- 
1.5.1.1-dirty

^ permalink raw reply related	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2007-04-25 22:47 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-04-25 15:48 [PATCH 0/7] Battery class, external power framework, ds2760 battery Anton Vorontsov
2007-04-25 15:50 ` [PATCH 1/7] External power framework Anton Vorontsov
2007-04-25 15:50 ` [PATCH 2/7] pda power driver Anton Vorontsov
2007-04-25 15:50 ` [PATCH 3/7] Universal battery class Anton Vorontsov
2007-04-25 15:51 ` [PATCH 4/7] APM emulation driver for class batteries Anton Vorontsov
2007-04-25 15:51 ` [PATCH 5/7] 1-Wire ds2760 chip battery driver Anton Vorontsov
2007-04-25 15:52 ` [PATCH 6/7] remove "#if 0" from find_bus function, export it Anton Vorontsov
2007-04-25 15:52 ` [PATCH 7/7] ds2760 W1 slave Anton Vorontsov
2007-04-25 20:05 ` [PATCH 0/7] Battery class, external power framework, ds2760 battery Andrew Morton
2007-04-25 21:05   ` Anton Vorontsov
2007-04-25 21:19     ` Anton Vorontsov
2007-04-25 22:47     ` Andrew Morton
  -- strict thread matches above, loose matches on Subject: below --
2007-04-20 11:57 [PATCH 1/7] External power framework Anton Vorontsov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox