blob: 56059475bc568732f23fc2e71902371a52eead3f [file] [log] [blame]
From linux-kernel-owner+greg=40kroah.com-S964915AbWJWSUm@vger.kernel.org Mon Oct 23 11: 22:06 2006
From: David Woodhouse <dwmw2@infradead.org>
Date: Mon, 23 Oct 2006 19:20:33 +0100
Cc: davidz@redhat.com, greg@kroah.com, mjg59@srcf.ucam.org, len.brown@intel.com, sfr@canb.auug.org.au, benh@kernel.crashing.org
Subject: Battery class driver.
Content-Type: text/plain; charset=UTF-8
To: linux-kernel@vger.kernel.org, olpc-dev@laptop.org
Mime-Version: 1.0
Message-Id: <1161627633.19446.387.camel@pmac.infradead.org>
Status: RO
Content-Length: 21135
Lines: 691
[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@infradead.org>
---
drivers/Kconfig | 2
drivers/Makefile | 1
drivers/battery/Kconfig | 22 +++
drivers/battery/Makefile | 4
drivers/battery/battery-class.c | 292 ++++++++++++++++++++++++++++++++++++++++
drivers/battery/olpc-battery.c | 198 +++++++++++++++++++++++++++
include/linux/battery.h | 84 +++++++++++
7 files changed, 603 insertions(+)
--- gregkh-2.6.orig/drivers/Kconfig
+++ gregkh-2.6/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"
--- gregkh-2.6.orig/drivers/Makefile
+++ gregkh-2.6/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/
--- /dev/null
+++ gregkh-2.6/drivers/battery/Kconfig
@@ -0,0 +1,22 @@
+
+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.
+
+config OLPC_BATTERY
+ tristate "One Laptop Per Child battery"
+ depends on BATTERY_CLASS && X86_32
+ help
+ Say Y to enable support for the battery on the $100 laptop.
+
+
+endmenu
--- /dev/null
+++ gregkh-2.6/drivers/battery/Makefile
@@ -0,0 +1,4 @@
+# Battery code
+obj-$(CONFIG_BATTERY_CLASS) += battery-class.o
+
+obj-$(CONFIG_OLPC_BATTERY) += olpc-battery.o
--- /dev/null
+++ gregkh-2.6/drivers/battery/battery-class.c
@@ -0,0 +1,292 @@
+/*
+ * Battery class core
+ *
+ * © 2006 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Based on LED Class support, by John Lenz and Richard Purdie:
+ *
+ * © 2005 John Lenz <lenz@cs.wisc.edu>
+ * © 2005-2006 Richard Purdie <rpurdie@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 device *dev,
+ struct device_attribute *dev_attr,
+ char *buf, int attr)
+{
+ struct battery_dev *battery_dev = dev_get_drvdata(dev);
+ ssize_t ret = 0;
+ long value;
+
+ ret = battery_dev->query_long(battery_dev, 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 device *dev,
+ struct device_attribute *dev_attr,
+ char *buf, int attr)
+{
+ struct battery_dev *battery_dev = dev_get_drvdata(dev);
+ ssize_t ret = 0;
+ long value;
+
+ ret = battery_dev->query_long(battery_dev, 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 device *dev,
+ struct device_attribute *dev_attr,
+ char *buf, int attr)
+{
+ struct battery_dev *battery_dev = dev_get_drvdata(dev);
+ ssize_t ret = 0;
+
+ ret = battery_dev->query_str(battery_dev, 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 device *dev,
+ struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct battery_dev *battery_dev = dev_get_drvdata(dev);
+ ssize_t ret = 0;
+ unsigned long status;
+
+ status = battery_dev->status(battery_dev, ~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 device *dev,
+ struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct battery_dev *battery_dev = dev_get_drvdata(dev);
+ ssize_t ret = 0;
+ unsigned long status;
+
+ status = battery_dev->status(battery_dev, 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 DEVICE_ATTR() if we want one named 'current' */
+#define BATTERY_DEVICE_ATTR(_name, _attr, _type) \
+static ssize_t battery_attr_show_##_attr(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ return battery_attribute_show_##_type(dev, attr, buf, BAT_INFO_##_attr); \
+} \
+static struct device_attribute dev_attr_##_attr = { \
+ .attr = { .name = _name, .mode = 0444, .owner = THIS_MODULE }, \
+ .show = battery_attr_show_##_attr };
+
+static DEVICE_ATTR(status,0444,battery_attribute_show_status, NULL);
+static 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_dev->capabilities & (1<<BAT_INFO_##_attr)) \
+ device_create_file(battery_dev->dev, &dev_attr_##_attr);
+#define UNREGISTER_ATTR(_attr) \
+ if (battery_dev->capabilities & (1<<BAT_INFO_##_attr)) \
+ device_remove_file(battery_dev->dev, &dev_attr_##_attr);
+/**
+ * battery_dev_register - register a new object of battery_dev class.
+ * @dev: The parent of the device to register.
+ * @battery_dev: the battery_dev structure for this device.
+ */
+int battery_dev_register(struct device *parent,
+ struct battery_dev *battery_dev)
+{
+ battery_dev->dev = device_create(battery_class, parent, 0,
+ "%s", battery_dev->name);
+
+ if (unlikely(IS_ERR(battery_dev->dev)))
+ return PTR_ERR(battery_dev->dev);
+
+ dev_set_drvdata(battery_dev->dev, battery_dev);
+
+ /* register the attributes */
+ device_create_file(battery_dev->dev, &dev_attr_status);
+
+ if (battery_dev->status_cap & (1<<BAT_STAT_AC))
+ device_create_file(battery_dev->dev, &dev_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);
+
+ dev_info(battery_dev->dev, "Registered battery device\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(battery_dev_register);
+
+/**
+ * battery_dev_unregister - unregisters a object of battery_properties class.
+ * @battery_dev: the battery device to unreigister
+ *
+ * Unregisters a previously registered via battery_dev_register object.
+ */
+void battery_dev_unregister(struct battery_dev *battery_dev)
+{
+ device_remove_file(battery_dev->dev, &dev_attr_status);
+
+ if (battery_dev->status_cap & (1<<BAT_STAT_AC))
+ device_remove_file(battery_dev->dev, &dev_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);
+
+ device_unregister(battery_dev->dev);
+}
+EXPORT_SYMBOL_GPL(battery_dev_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@infradead.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Battery class interface");
--- /dev/null
+++ gregkh-2.6/drivers/battery/olpc-battery.c
@@ -0,0 +1,198 @@
+/*
+ * Battery driver for One Laptop Per Child ($100 laptop) board.
+ *
+ * © 2006 David Woodhouse <dwmw2@infradead.org>
+ *
+ * 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>
+#include <asm/io.h>
+
+#define wBAT_VOLTAGE 0xf900 /* *9.76/32, mV */
+#define wBAT_CURRENT 0xf902 /* *15.625/120, mA */
+#define wBAT_TEMP 0xf906 /* *256/1000, °C */
+#define wAMB_TEMP 0xf908 /* *256/1000, °C */
+#define SOC 0xf910 /* percentage */
+#define sMBAT_STATUS 0xfaa4
+#define sBAT_PRESENT 1
+#define sBAT_FULL 2
+#define sBAT_DESTROY 4
+#define sBAT_LOW 32
+#define sBAT_DISCHG 64
+#define sMCHARGE_STATUS 0xfaa5
+#define sBAT_CHARGE 1
+#define sBAT_OVERTEMP 4
+#define sBAT_NiMH 8
+#define sPOWER_FLAG 0xfa40
+#define ADAPTER_IN 1
+
+static int lock_ec(void)
+{
+ unsigned long timeo = jiffies + HZ/20;
+
+ while (1) {
+ unsigned char lock = inb(0x6c) & 0x80;
+ if (!lock)
+ return 0;
+ if (time_after(jiffies, timeo))
+ return 1;
+ yield();
+ }
+}
+
+static void unlock_ec(void)
+{
+ outb(0xff, 0x6c);
+}
+
+unsigned char read_ec_byte(unsigned short adr)
+{
+ outb(adr >> 8, 0x381);
+ outb(adr, 0x382);
+ return inb(0x383);
+}
+
+unsigned short read_ec_word(unsigned short adr)
+{
+ return (read_ec_byte(adr) << 8) | read_ec_byte(adr+1);
+}
+
+unsigned long olpc_bat_status(struct battery_dev *dev, unsigned long mask)
+{
+ unsigned long result = 0;
+ unsigned short tmp;
+
+ if (lock_ec()) {
+ printk(KERN_ERR "Failed to lock EC for battery access\n");
+ return BAT_STAT_ERROR;
+ }
+
+ if (mask & BAT_STAT_AC) {
+ if (read_ec_byte(sPOWER_FLAG) & ADAPTER_IN)
+ result |= BAT_STAT_AC;
+ }
+ if (mask & (BAT_STAT_PRESENT|BAT_STAT_FULL|BAT_STAT_FIRE|BAT_STAT_LOW|BAT_STAT_DISCHARGING)) {
+ tmp = read_ec_byte(sMBAT_STATUS);
+
+ if (tmp & sBAT_PRESENT)
+ result |= BAT_STAT_PRESENT;
+ if (tmp & sBAT_FULL)
+ result |= BAT_STAT_FULL;
+ if (tmp & sBAT_DESTROY)
+ result |= BAT_STAT_FIRE;
+ if (tmp & sBAT_LOW)
+ result |= BAT_STAT_LOW;
+ if (tmp & sBAT_DISCHG)
+ result |= BAT_STAT_DISCHARGING;
+ }
+ if (mask & (BAT_STAT_CHARGING|BAT_STAT_OVERTEMP)) {
+ tmp = read_ec_byte(sMCHARGE_STATUS);
+ if (tmp & sBAT_CHARGE)
+ result |= BAT_STAT_CHARGING;
+ if (tmp & sBAT_OVERTEMP)
+ result |= BAT_STAT_OVERTEMP;
+ }
+ unlock_ec();
+ return result;
+}
+
+int olpc_bat_query_long(struct battery_dev *dev, int attr, long *result)
+{
+ int ret = 0;
+
+ if (lock_ec())
+ return -EIO;
+
+ if (!(read_ec_byte(sMBAT_STATUS) & sBAT_PRESENT)) {
+ ret = -ENODEV;
+ } else if (attr == BAT_INFO_VOLTAGE) {
+ *result = read_ec_word(wBAT_VOLTAGE) * 9760 / 32000;
+ } else if (attr == BAT_INFO_CURRENT) {
+ *result = read_ec_word(wBAT_CURRENT) * 15625 / 120000;
+ } else if (attr == BAT_INFO_TEMP1) {
+ *result = read_ec_word(wBAT_TEMP) * 1000 / 256;
+ } else if (attr == BAT_INFO_TEMP2) {
+ *result = read_ec_word(wAMB_TEMP) * 1000 / 256;
+ } else if (attr == BAT_INFO_CHARGE_PCT) {
+ *result = read_ec_byte(SOC);
+ } else
+ ret = -EINVAL;
+
+ unlock_ec();
+ return ret;
+}
+
+int olpc_bat_query_str(struct battery_dev *dev, int attr, char *str, int len)
+{
+ int ret = 0;
+
+ if (attr == BAT_INFO_TYPE) {
+ snprintf(str, len, "OLPC");
+ } else if (attr == BAT_INFO_TEMP1_NAME) {
+ snprintf(str, len, "battery");
+ } else if (attr == BAT_INFO_TEMP2_NAME) {
+ snprintf(str, len, "ambient");
+ } else if (!(read_ec_byte(sMBAT_STATUS) & sBAT_PRESENT)) {
+ ret = -ENODEV;
+ } else if (attr == BAT_INFO_TECHNOLOGY) {
+ if (lock_ec())
+ ret = -EIO;
+ else {
+ unsigned short tmp = read_ec_byte(sMCHARGE_STATUS);
+ if (tmp & sBAT_NiMH)
+ snprintf(str, len, "NiMH");
+ else
+ snprintf(str, len, "unknown");
+ }
+ unlock_ec();
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static struct battery_dev olpc_bat = {
+ .name = "OLPC",
+ .capabilities = (1<<BAT_INFO_VOLTAGE) |
+ (1<<BAT_INFO_CURRENT) |
+ (1<<BAT_INFO_TEMP1) |
+ (1<<BAT_INFO_TEMP2) |
+ (1<<BAT_INFO_CHARGE_PCT) |
+ (1<<BAT_INFO_TYPE) |
+ (1<<BAT_INFO_TECHNOLOGY) |
+ (1<<BAT_INFO_TEMP1_NAME) |
+ (1<<BAT_INFO_TEMP2_NAME),
+ .status_cap = BAT_STAT_AC | BAT_STAT_PRESENT | BAT_STAT_LOW |
+ BAT_STAT_FULL | BAT_STAT_CHARGING| BAT_STAT_DISCHARGING |
+ BAT_STAT_OVERTEMP | BAT_STAT_FIRE,
+ .status = olpc_bat_status,
+ .query_long = olpc_bat_query_long,
+ .query_str = olpc_bat_query_str,
+};
+
+void __exit olpc_bat_exit(void)
+{
+ battery_dev_unregister(&olpc_bat);
+}
+
+int __init olpc_bat_init(void)
+{
+ battery_dev_register(NULL, &olpc_bat);
+ return 0;
+}
+
+module_init(olpc_bat_init);
+module_exit(olpc_bat_exit);
+
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Battery class interface");
--- /dev/null
+++ gregkh-2.6/include/linux/battery.h
@@ -0,0 +1,84 @@
+/*
+ * Driver model for batteries
+ *
+ * © 2006 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Based on LED Class support, by John Lenz and Richard Purdie:
+ *
+ * © 2005 John Lenz <lenz@cs.wisc.edu>
+ * © 2005-2006 Richard Purdie <rpurdie@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_dev {
+ const char *name;
+ /* Capabilities of this battery driver */
+ unsigned long capabilities, status_cap;
+
+ /* Query functions */
+ unsigned long (*status)(struct battery_dev *, unsigned long mask);
+ int (*query_long)(struct battery_dev *, int attr, long *result);
+ int (*query_str)(struct battery_dev *, int attr, char *str, ssize_t len);
+
+ struct device *dev;
+ struct list_head node; /* Battery Device list */
+};
+
+extern int battery_dev_register(struct device *parent,
+ struct battery_dev *battery_dev);
+extern void battery_dev_unregister(struct battery_dev *battery_dev);
+
+#endif /* __LINUX_BATTERY_H__ */