[BATTERY] Add initial implementation of battery class

David Woodhouse dwmw2 at infradead.org
Tue Nov 7 22:38:26 EST 2006


Commit:     6cbec3b84e3ce737b4217788841ea10a28a5e340
Parent:     73441c665bee555526b1cf3eef603a0cff0b7e19
commit 6cbec3b84e3ce737b4217788841ea10a28a5e340
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Mon Oct 23 18:14:14 2006 +0100
Commit:     David Woodhouse <dwmw2 at infradead.org>
CommitDate: Mon Oct 23 18:14:14 2006 +0100

    [BATTERY] Add initial implementation of battery class
    
    I really don't like the sysfs interaction, and I don't much like the
    internal interaction with the battery drivers either. In fact, there
    isn't much I _do_ like, but it's good enough as a straw man.
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/Kconfig                 |    2 
 drivers/Makefile                |    1 
 drivers/battery/Kconfig         |   15 ++
 drivers/battery/Makefile        |    2 
 drivers/battery/battery-class.c |  286 +++++++++++++++++++++++++++++++++++++++
 include/linux/battery.h         |   84 +++++++++++
 6 files changed, 390 insertions(+), 0 deletions(-)

diff --git a/drivers/Kconfig b/drivers/Kconfig
index f394634..1dc5dbe 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -34,6 +34,8 @@ source "drivers/ieee1394/Kconfig"
 
 source "drivers/message/i2o/Kconfig"
 
+source "drivers/battery/Kconfig"
+
 source "drivers/macintosh/Kconfig"
 
 source "drivers/net/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 4ac14da..cd091c9 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_PARPORT)		+= parport/
 obj-y				+= base/ block/ misc/ mfd/ net/ media/
 obj-$(CONFIG_NUBUS)		+= nubus/
 obj-$(CONFIG_ATM)		+= atm/
+obj-$(CONFIG_BATTERY_CLASS)	+= battery/
 obj-$(CONFIG_PPC_PMAC)		+= macintosh/
 obj-$(CONFIG_IDE)		+= ide/
 obj-$(CONFIG_FC4)		+= fc4/
diff --git a/drivers/battery/Kconfig b/drivers/battery/Kconfig
new file mode 100644
index 0000000..29d630f
--- /dev/null
+++ b/drivers/battery/Kconfig
@@ -0,0 +1,15 @@
+
+menu "Battery support"
+
+config BATTERY_CLASS
+       tristate "Battery support"
+       help
+         Say Y to enable battery class support. This allows a battery
+	 information to be presented in a uniform manner for all types
+	 of batteries.
+
+	 Battery information from APM and ACPI is not yet available by
+	 this method, but should soon be. If you use APM or ACPI, say
+	 'N', although saying 'Y' would be harmless.
+
+endmenu
diff --git a/drivers/battery/Makefile b/drivers/battery/Makefile
new file mode 100644
index 0000000..c0ac6ef
--- /dev/null
+++ b/drivers/battery/Makefile
@@ -0,0 +1,2 @@
+# Battery code
+obj-$(CONFIG_BATTERY_CLASS)		+= battery-class.o
diff --git a/drivers/battery/battery-class.c b/drivers/battery/battery-class.c
new file mode 100644
index 0000000..c037ba8
--- /dev/null
+++ b/drivers/battery/battery-class.c
@@ -0,0 +1,286 @@
+/*
+ * Battery class core
+ *
+ *	© 2006 David Woodhouse <dwmw2 at infradead.org>
+ *
+ * Based on LED Class support, by John Lenz and Richard Purdie:
+ *
+ *	© 2005 John Lenz <lenz at cs.wisc.edu>
+ *	© 2005-2006 Richard Purdie <rpurdie at openedhand.com>
+ *
+ * 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/kernel.h>
+#include <linux/device.h>
+#include <linux/battery.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+
+static struct class *battery_class;
+
+/* OMFG we can't just have a single 'show' routine which is given the
+   'class_attribute' as an argument -- we have to have 20-odd copies
+   of almost identical routines */
+
+static ssize_t battery_attribute_show_int(struct class_device *dev, char *buf, int attr)
+{
+        struct battery_classdev *battery_cdev = class_get_devdata(dev);
+        ssize_t ret = 0;
+	long value;
+
+	ret =  battery_cdev->query_long(battery_cdev, attr, &value);
+	if (ret)
+		return ret;
+
+	sprintf(buf, "%ld\n", value);
+        ret = strlen(buf) + 1;
+
+        return ret;
+}  
+
+static ssize_t battery_attribute_show_milli(struct class_device *dev, char *buf, int attr)
+{
+        struct battery_classdev *battery_cdev = class_get_devdata(dev);
+        ssize_t ret = 0;
+	long value;
+
+	ret = battery_cdev->query_long(battery_cdev, attr, &value);
+	if (ret)
+		return ret;
+
+	sprintf(buf, "%ld.%03ld\n", value/1000, value % 1000);
+        ret = strlen(buf) + 1;
+        return ret;
+}  
+
+static ssize_t battery_attribute_show_string(struct class_device *dev, char *buf, int attr)
+{
+        struct battery_classdev *battery_cdev = class_get_devdata(dev);
+        ssize_t ret = 0;
+
+	ret = battery_cdev->query_str(battery_cdev, attr, buf, PAGE_SIZE-1);
+	if (ret)
+		return ret;
+
+	strcat(buf, "\n");
+        ret = strlen(buf) + 1;
+        return ret;
+}  
+
+static ssize_t battery_attribute_show_status(struct class_device *dev, char *buf)
+{
+        struct battery_classdev *battery_cdev = class_get_devdata(dev);
+        ssize_t ret = 0;
+	unsigned long status;
+
+	status = battery_cdev->status(battery_cdev, ~BAT_STAT_AC);
+	if (status & BAT_STAT_ERROR)
+		return -EIO;
+
+	if (status & BAT_STAT_PRESENT)
+		sprintf(buf, "present");
+	else
+		sprintf(buf, "absent");
+
+	if (status & BAT_STAT_LOW)
+		strcat(buf, ",low");
+
+	if (status & BAT_STAT_FULL)
+		strcat(buf, ",full");
+
+	if (status & BAT_STAT_CHARGING)
+		strcat(buf, ",charging");
+
+	if (status & BAT_STAT_DISCHARGING)
+		strcat(buf, ",discharging");
+
+	if (status & BAT_STAT_OVERTEMP)
+		strcat(buf, ",overtemp");
+
+	if (status & BAT_STAT_FIRE)
+		strcat(buf, ",on-fire");
+
+	if (status & BAT_STAT_CHARGE_DONE)
+		strcat(buf, ",charge-done");
+
+	strcat(buf, "\n");
+        ret = strlen(buf) + 1;
+        return ret;
+}
+
+static ssize_t battery_attribute_show_ac_status(struct class_device *dev, char *buf)
+{
+        struct battery_classdev *battery_cdev = class_get_devdata(dev);
+        ssize_t ret = 0;
+	unsigned long status;
+
+	status = battery_cdev->status(battery_cdev, BAT_STAT_AC);
+	if (status & BAT_STAT_ERROR)
+		return -EIO;
+
+	if (status & BAT_STAT_AC)
+		sprintf(buf, "on-line");
+	else
+		sprintf(buf, "off-line");
+
+	strcat(buf, "\n");
+        ret = strlen(buf) + 1;
+        return ret;
+}  
+
+/* Ew. We can't even use CLASS_DEVICE_ATTR() if we want one named 'current' */
+#define BATTERY_DEVICE_ATTR(_name, _attr, _type) \
+static ssize_t battery_attr_show_##_attr(struct class_device *dev, char *buf)	\
+{										\
+	return battery_attribute_show_##_type(dev, buf, BAT_INFO_##_attr);	\
+}										\
+static struct class_device_attribute class_device_attr_##_attr  = {		\
+        .attr = { .name = _name, .mode = 0444, .owner = THIS_MODULE },		\
+	.show = battery_attr_show_##_attr };
+
+static CLASS_DEVICE_ATTR(status,0444,battery_attribute_show_status, NULL);
+static CLASS_DEVICE_ATTR(ac,0444,battery_attribute_show_ac_status, NULL);
+BATTERY_DEVICE_ATTR("temp1",TEMP1,milli);
+BATTERY_DEVICE_ATTR("temp1_name",TEMP1_NAME,string);
+BATTERY_DEVICE_ATTR("temp2",TEMP2,milli);
+BATTERY_DEVICE_ATTR("temp2_name",TEMP2_NAME,string);
+BATTERY_DEVICE_ATTR("voltage",VOLTAGE,milli);
+BATTERY_DEVICE_ATTR("voltage_design",VOLTAGE_DESIGN,milli);
+BATTERY_DEVICE_ATTR("current",CURRENT,milli);
+BATTERY_DEVICE_ATTR("charge_rate",CHARGE_RATE,milli);
+BATTERY_DEVICE_ATTR("charge_max",CHARGE_MAX,milli);
+BATTERY_DEVICE_ATTR("charge_last",CHARGE_LAST,milli);
+BATTERY_DEVICE_ATTR("charge_low",CHARGE_LOW,milli);
+BATTERY_DEVICE_ATTR("charge_warn",CHARGE_WARN,milli);
+BATTERY_DEVICE_ATTR("charge_unit",CHARGE_UNITS,string);
+BATTERY_DEVICE_ATTR("charge_percent",CHARGE_PCT,int);
+BATTERY_DEVICE_ATTR("time_remaining",TIME_REMAINING,int);
+BATTERY_DEVICE_ATTR("manufacturer",MANUFACTURER,string);
+BATTERY_DEVICE_ATTR("technology",TECHNOLOGY,string);
+BATTERY_DEVICE_ATTR("model",MODEL,string);
+BATTERY_DEVICE_ATTR("serial",SERIAL,string);
+BATTERY_DEVICE_ATTR("type",TYPE,string);
+BATTERY_DEVICE_ATTR("oem_info",OEM_INFO,string);
+
+#define REGISTER_ATTR(_attr)							\
+	if (battery_cdev->capabilities & (1<<BAT_INFO_##_attr))			\
+		class_device_create_file(battery_cdev->class_dev,		\
+					 &class_device_attr_##_attr);
+#define UNREGISTER_ATTR(_attr)							\
+	if (battery_cdev->capabilities & (1<<BAT_INFO_##_attr))			\
+		class_device_remove_file(battery_cdev->class_dev,		\
+					 &class_device_attr_##_attr);
+/**
+ * battery_classdev_register - register a new object of battery_classdev class.
+ * @dev: The device to register.
+ * @battery_cdev: the battery_classdev structure for this device.
+ */
+int battery_classdev_register(struct device *parent, struct battery_classdev *battery_cdev)
+{
+        battery_cdev->class_dev = class_device_create(battery_class, NULL, 0,
+						      parent, "%s", battery_cdev->name);
+
+        if (unlikely(IS_ERR(battery_cdev->class_dev)))
+                return PTR_ERR(battery_cdev->class_dev);
+
+        class_set_devdata(battery_cdev->class_dev, battery_cdev);
+
+        /* register the attributes */
+        class_device_create_file(battery_cdev->class_dev,
+                                &class_device_attr_status);
+
+        if (battery_cdev->status_cap & (1<<BAT_STAT_AC))
+		class_device_create_file(battery_cdev->class_dev, &class_device_attr_ac);
+
+	REGISTER_ATTR(TEMP1);
+	REGISTER_ATTR(TEMP1_NAME);
+	REGISTER_ATTR(TEMP2);
+	REGISTER_ATTR(TEMP2_NAME);
+	REGISTER_ATTR(VOLTAGE);
+	REGISTER_ATTR(VOLTAGE_DESIGN);
+	REGISTER_ATTR(CHARGE_PCT);
+	REGISTER_ATTR(CURRENT);
+	REGISTER_ATTR(CHARGE_RATE);
+	REGISTER_ATTR(CHARGE_MAX);
+	REGISTER_ATTR(CHARGE_LAST);
+	REGISTER_ATTR(CHARGE_LOW);
+	REGISTER_ATTR(CHARGE_WARN);
+	REGISTER_ATTR(CHARGE_UNITS);
+	REGISTER_ATTR(TIME_REMAINING);
+	REGISTER_ATTR(MANUFACTURER);
+	REGISTER_ATTR(TECHNOLOGY);
+	REGISTER_ATTR(MODEL);
+	REGISTER_ATTR(SERIAL);
+	REGISTER_ATTR(TYPE);
+	REGISTER_ATTR(OEM_INFO);
+
+        printk(KERN_INFO "Registered battery device: %s\n",
+                        battery_cdev->class_dev->class_id);
+
+        return 0;
+}
+EXPORT_SYMBOL_GPL(battery_classdev_register);
+
+/**
+ * battery_classdev_unregister - unregisters a object of battery_properties class.
+ * @battery_cdev: the battery device to unreigister
+ *
+ * Unregisters a previously registered via battery_classdev_register object.
+ */
+void battery_classdev_unregister(struct battery_classdev *battery_cdev)
+{
+        class_device_remove_file(battery_cdev->class_dev,
+				 &class_device_attr_status);
+
+        if (battery_cdev->status_cap & (1<<BAT_STAT_AC))
+		class_device_remove_file(battery_cdev->class_dev, &class_device_attr_ac);
+
+	UNREGISTER_ATTR(TEMP1);
+	UNREGISTER_ATTR(TEMP1_NAME);
+	UNREGISTER_ATTR(TEMP2);
+	UNREGISTER_ATTR(TEMP2_NAME);
+	UNREGISTER_ATTR(VOLTAGE);
+	UNREGISTER_ATTR(VOLTAGE_DESIGN);
+	UNREGISTER_ATTR(CHARGE_PCT);
+	UNREGISTER_ATTR(CURRENT);
+	UNREGISTER_ATTR(CHARGE_RATE);
+	UNREGISTER_ATTR(CHARGE_MAX);
+	UNREGISTER_ATTR(CHARGE_LAST);
+	UNREGISTER_ATTR(CHARGE_LOW);
+	UNREGISTER_ATTR(CHARGE_WARN);
+	UNREGISTER_ATTR(CHARGE_UNITS);
+	UNREGISTER_ATTR(TIME_REMAINING);
+	UNREGISTER_ATTR(MANUFACTURER);
+	UNREGISTER_ATTR(TECHNOLOGY);
+	UNREGISTER_ATTR(MODEL);
+	UNREGISTER_ATTR(SERIAL);
+	UNREGISTER_ATTR(TYPE);
+	UNREGISTER_ATTR(OEM_INFO);
+
+        class_device_unregister(battery_cdev->class_dev);
+}
+EXPORT_SYMBOL_GPL(battery_classdev_unregister);
+
+static int __init battery_init(void)
+{
+        battery_class = class_create(THIS_MODULE, "battery");
+        if (IS_ERR(battery_class))
+                return PTR_ERR(battery_class);
+        return 0;
+}
+
+static void __exit battery_exit(void)
+{
+        class_destroy(battery_class);
+}
+
+subsys_initcall(battery_init);
+module_exit(battery_exit);
+
+MODULE_AUTHOR("David Woodhouse <dwmw2 at infradead.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Battery class interface");
diff --git a/include/linux/battery.h b/include/linux/battery.h
new file mode 100644
index 0000000..9a7eda4
--- /dev/null
+++ b/include/linux/battery.h
@@ -0,0 +1,84 @@
+/*
+ * Driver model for batteries
+ *
+ *	© 2006 David Woodhouse <dwmw2 at infradead.org>
+ *
+ * Based on LED Class support, by John Lenz and Richard Purdie:
+ *
+ *	© 2005 John Lenz <lenz at cs.wisc.edu>
+ *	© 2005-2006 Richard Purdie <rpurdie at openedhand.com>
+ *
+ * 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 __LINUX_BATTERY_H__
+#define __LINUX_BATTERY_H__
+
+struct device;
+struct class_device;
+
+/*
+ * Battery Core
+ */
+#define BAT_STAT_AC		(1<<0)
+#define BAT_STAT_PRESENT	(1<<1)
+#define BAT_STAT_LOW		(1<<2)
+#define BAT_STAT_FULL		(1<<3)
+#define BAT_STAT_CHARGING	(1<<4)
+#define BAT_STAT_DISCHARGING	(1<<5)
+#define BAT_STAT_OVERTEMP	(1<<6)
+#define BAT_STAT_FIRE		(1<<7)
+#define BAT_STAT_CHARGE_DONE	(1<<8)
+
+#define BAT_STAT_ERROR		(1<<31)
+
+#define BAT_INFO_TEMP1		(0) /* °C/1000 */
+#define BAT_INFO_TEMP1_NAME	(1) /* string */
+
+#define BAT_INFO_TEMP2		(2) /* °C/1000 */
+#define BAT_INFO_TEMP2_NAME	(3) /* string */
+
+#define BAT_INFO_VOLTAGE	(4) /* mV */
+#define BAT_INFO_VOLTAGE_DESIGN	(5) /* mV */
+
+#define BAT_INFO_CURRENT	(6) /* mA */
+
+#define BAT_INFO_CHARGE_RATE	(7) /* BAT_INFO_CHARGE_UNITS */
+#define BAT_INFO_CHARGE		(8) /* BAT_INFO_CHARGE_UNITS */
+#define BAT_INFO_CHARGE_MAX	(9) /* BAT_INFO_CHARGE_UNITS  */
+#define BAT_INFO_CHARGE_LAST	(10) /* BAT_INFO_CHARGE_UNITS  */
+#define BAT_INFO_CHARGE_LOW	(11) /* BAT_INFO_CHARGE_UNITS  */
+#define BAT_INFO_CHARGE_WARN	(12) /* BAT_INFO_CHARGE_UNITS  */
+#define BAT_INFO_CHARGE_UNITS	(13) /* string */
+#define BAT_INFO_CHARGE_PCT	(14) /* % */
+
+#define BAT_INFO_TIME_REMAINING	(15) /* seconds */
+
+#define BAT_INFO_MANUFACTURER	(16) /* string */
+#define BAT_INFO_TECHNOLOGY	(17) /* string */
+#define BAT_INFO_MODEL		(18) /* string */
+#define BAT_INFO_SERIAL		(19) /* string */
+#define BAT_INFO_TYPE		(20) /* string */
+#define BAT_INFO_OEM_INFO	(21) /* string */
+
+struct battery_classdev {
+	const char		*name;
+	/* Capabilities of this battery driver */
+	unsigned long		 capabilities, status_cap;
+
+	/* Query functions */
+	unsigned long		(*status)(struct battery_classdev *, unsigned long mask);
+	int			(*query_long)(struct battery_classdev *, int attr, long *result);
+	int			(*query_str)(struct battery_classdev *, int attr, char *str, ssize_t len);
+
+	struct class_device	*class_dev;
+	struct list_head	 node;	/* Battery Device list */
+};
+
+extern int battery_classdev_register(struct device *parent,
+				     struct battery_classdev *battery_cdev);
+extern void battery_classdev_unregister(struct battery_classdev *battery_cdev);
+
+#endif /* __LINUX_BATTERY_H__ */


More information about the Commits-kernel mailing list