| 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__ */ |