[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