aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Vorontsov <cbou@mail.ru>2007-05-03 16:27:45 -0400
committerDavid Woodhouse <dwmw2@infradead.org>2007-07-10 06:25:44 -0400
commit4a11b59d8283662193a9c6a9c14c58d1b9bf0617 (patch)
tree492bf022c806ec6c554ca5f8e7c05ea6db138137
parent7dcca30a32aadb0520417521b0c44f42d09fe05c (diff)
[BATTERY] Universal power supply class (was: battery class)
This class is result of "external power" and "battery" classes merge, as suggested by David Woodhouse. He also implemented uevent support. Here how userspace seeing it now: # ls /sys/class/power\ supply/ ac main-battery usb # cat /sys/class/power\ supply/ac/type AC # cat /sys/class/power\ supply/usb/type USB # cat /sys/class/power\ supply/main-battery/type Battery # cat /sys/class/power\ supply/ac/online 1 # cat /sys/class/power\ supply/usb/online 0 # cat /sys/class/power\ supply/main-battery/status Charging # cat /sys/class/leds/h5400\:red-left/trigger none h5400-radio timer hwtimer ac-online usb-online main-battery-charging-or-full [main-battery-charging] main-battery-full Signed-off-by: Anton Vorontsov <cbou@mail.ru> Signed-off-by: David Woodhouse <dwmw2@infradead.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-rw-r--r--Documentation/power_supply_class.txt167
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/power/Kconfig17
-rw-r--r--drivers/power/Makefile15
-rw-r--r--drivers/power/power_supply.h42
-rw-r--r--drivers/power/power_supply_core.c168
-rw-r--r--drivers/power/power_supply_leds.c176
-rw-r--r--drivers/power/power_supply_sysfs.c299
-rw-r--r--include/linux/power_supply.h180
10 files changed, 1067 insertions, 0 deletions
diff --git a/Documentation/power_supply_class.txt b/Documentation/power_supply_class.txt
new file mode 100644
index 00000000000..9758cf433c0
--- /dev/null
+++ b/Documentation/power_supply_class.txt
@@ -0,0 +1,167 @@
1Linux power supply class
2========================
3
4Synopsis
5~~~~~~~~
6Power supply class used to represent battery, UPS, AC or DC power supply
7properties to user-space.
8
9It defines core set of attributes, which should be applicable to (almost)
10every power supply out there. Attributes are available via sysfs and uevent
11interfaces.
12
13Each attribute has well defined meaning, up to unit of measure used. While
14the attributes provided are believed to be universally applicable to any
15power supply, specific monitoring hardware may not be able to provide them
16all, so any of them may be skipped.
17
18Power supply class is extensible, and allows to define drivers own attributes.
19The core attribute set is subject to the standard Linux evolution (i.e.
20if it will be found that some attribute is applicable to many power supply
21types or their drivers, it can be added to the core set).
22
23It also integrates with LED framework, for the purpose of providing
24typically expected feedback of battery charging/fully charged status and
25AC/USB power supply online status. (Note that specific details of the
26indication (including whether to use it at all) are fully controllable by
27user and/or specific machine defaults, per design principles of LED
28framework).
29
30
31Attributes/properties
32~~~~~~~~~~~~~~~~~~~~~
33Power supply class has predefined set of attributes, this eliminates code
34duplication across drivers. Power supply class insist on reusing its
35predefined attributes *and* their units.
36
37So, userspace gets predictable set of attributes and their units for any
38kind of power supply, and can process/present them to a user in consistent
39manner. Results for different power supplies and machines are also directly
40comparable.
41
42See drivers/power/ds2760_battery.c and drivers/power/pda_power.c for the
43example how to declare and handle attributes.
44
45
46Units
47~~~~~
48Quoting include/linux/power_supply.h:
49
50 All voltages, currents, charges, energies, time and temperatures in µV,
51 µA, µAh, µWh, seconds and tenths of degree Celsius unless otherwise
52 stated. It's driver's job to convert its raw values to units in which
53 this class operates.
54
55
56Attributes/properties detailed
57~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
58
59~ ~ ~ ~ ~ ~ ~ Charge/Energy/Capacity - how to not confuse ~ ~ ~ ~ ~ ~ ~
60~ ~
61~ Because both "charge" (µAh) and "energy" (µWh) represents "capacity" ~
62~ of battery, this class distinguish these terms. Don't mix them! ~
63~ ~
64~ CHARGE_* attributes represents capacity in µAh only. ~
65~ ENERGY_* attributes represents capacity in µWh only. ~
66~ CAPACITY attribute represents capacity in *percents*, from 0 to 100. ~
67~ ~
68~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
69
70Postfixes:
71_AVG - *hardware* averaged value, use it if your hardware is really able to
72report averaged values.
73_NOW - momentary/instantaneous values.
74
75STATUS - this attribute represents operating status (charging, full,
76discharging (i.e. powering a load), etc.). This corresponds to
77BATTERY_STATUS_* values, as defined in battery.h.
78
79HEALTH - represents health of the battery, values corresponds to
80POWER_SUPPLY_HEALTH_*, defined in battery.h.
81
82VOLTAGE_MAX_DESIGN, VOLTAGE_MIN_DESIGN - design values for maximal and
83minimal power supply voltages. Maximal/minimal means values of voltages
84when battery considered "full"/"empty" at normal conditions. Yes, there is
85no direct relation between voltage and battery capacity, but some dumb
86batteries use voltage for very approximated calculation of capacity.
87Battery driver also can use this attribute just to inform userspace
88about maximal and minimal voltage thresholds of a given battery.
89
90CHARGE_FULL_DESIGN, CHARGE_EMPTY_DESIGN - design charge values, when
91battery considered full/empty.
92
93ENERGY_FULL_DESIGN, ENERGY_EMPTY_DESIGN - same as above but for energy.
94
95CHARGE_FULL, CHARGE_EMPTY - These attributes means "last remembered value
96of charge when battery became full/empty". It also could mean "value of
97charge when battery considered full/empty at given conditions (temperature,
98age)". I.e. these attributes represents real thresholds, not design values.
99
100ENERGY_FULL, ENERGY_EMPTY - same as above but for energy.
101
102CAPACITY - capacity in percents.
103CAPACITY_LEVEL - capacity level. This corresponds to
104POWER_SUPPLY_CAPACITY_LEVEL_*.
105
106TEMP - temperature of the power supply.
107TEMP_AMBIENT - ambient temperature.
108
109TIME_TO_EMPTY - seconds left for battery to be considered empty (i.e.
110while battery powers a load)
111TIME_TO_FULL - seconds left for battery to be considered full (i.e.
112while battery is charging)
113
114
115Battery <-> external power supply interaction
116~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
117Often power supplies are acting as supplies and supplicants at the same
118time. Batteries are good example. So, batteries usually care if they're
119externally powered or not.
120
121For that case, power supply class implements notification mechanism for
122batteries.
123
124External power supply (AC) lists supplicants (batteries) names in
125"supplied_to" struct member, and each power_supply_changed() call
126issued by external power supply will notify supplicants via
127external_power_changed callback.
128
129
130QA
131~~
132Q: Where is POWER_SUPPLY_PROP_XYZ attribute?
133A: If you cannot find attribute suitable for your driver needs, feel free
134 to add it and send patch along with your driver.
135
136 The attributes available currently are the ones currently provided by the
137 drivers written.
138
139 Good candidates to add in future: model/part#, cycle_time, manufacturer,
140 etc.
141
142
143Q: I have some very specific attribute (e.g. battery color), should I add
144 this attribute to standard ones?
145A: Most likely, no. Such attribute can be placed in the driver itself, if
146 it is useful. Of course, if the attribute in question applicable to
147 large set of batteries, provided by many drivers, and/or comes from
148 some general battery specification/standard, it may be a candidate to
149 be added to the core attribute set.
150
151
152Q: Suppose, my battery monitoring chip/firmware does not provides capacity
153 in percents, but provides charge_{now,full,empty}. Should I calculate
154 percentage capacity manually, inside the driver, and register CAPACITY
155 attribute? The same question about time_to_empty/time_to_full.
156A: Most likely, no. This class is designed to export properties which are
157 directly measurable by the specific hardware available.
158
159 Inferring not available properties using some heuristics or mathematical
160 model is not subject of work for a battery driver. Such functionality
161 should be factored out, and in fact, apm_power, the driver to serve
162 legacy APM API on top of power supply class, uses a simple heuristic of
163 approximating remaining battery capacity based on its charge, current,
164 voltage and so on. But full-fledged battery model is likely not subject
165 for kernel at all, as it would require floating point calculation to deal
166 with things like differential equations and Kalman filters. This is
167 better be handled by batteryd/libbattery, yet to be written.
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 050323fd79e..c546de307ca 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -54,6 +54,8 @@ source "drivers/spi/Kconfig"
54 54
55source "drivers/w1/Kconfig" 55source "drivers/w1/Kconfig"
56 56
57source "drivers/power/Kconfig"
58
57source "drivers/hwmon/Kconfig" 59source "drivers/hwmon/Kconfig"
58 60
59source "drivers/mfd/Kconfig" 61source "drivers/mfd/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index adad2f3d438..503d8256944 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_I2O) += message/
61obj-$(CONFIG_RTC_LIB) += rtc/ 61obj-$(CONFIG_RTC_LIB) += rtc/
62obj-y += i2c/ 62obj-y += i2c/
63obj-$(CONFIG_W1) += w1/ 63obj-$(CONFIG_W1) += w1/
64obj-$(CONFIG_POWER_SUPPLY) += power/
64obj-$(CONFIG_HWMON) += hwmon/ 65obj-$(CONFIG_HWMON) += hwmon/
65obj-$(CONFIG_PHONE) += telephony/ 66obj-$(CONFIG_PHONE) += telephony/
66obj-$(CONFIG_MD) += md/ 67obj-$(CONFIG_MD) += md/
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
new file mode 100644
index 00000000000..7811fa62741
--- /dev/null
+++ b/drivers/power/Kconfig
@@ -0,0 +1,17 @@
1menuconfig POWER_SUPPLY
2 tristate "Power supply class support"
3 help
4 Say Y here to enable power supply class support. This allows
5 power supply (batteries, AC, USB) monitoring by userspace
6 via sysfs and uevent (if available) and/or APM kernel interface
7 (if selected below).
8
9if POWER_SUPPLY
10
11config POWER_SUPPLY_DEBUG
12 bool "Power supply debug"
13 help
14 Say Y here to enable debugging messages for power supply class
15 and drivers.
16
17endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
new file mode 100644
index 00000000000..3c88148983a
--- /dev/null
+++ b/drivers/power/Makefile
@@ -0,0 +1,15 @@
1power_supply-objs := power_supply_core.o
2
3ifeq ($(CONFIG_SYSFS),y)
4power_supply-objs += power_supply_sysfs.o
5endif
6
7ifeq ($(CONFIG_LEDS_TRIGGERS),y)
8power_supply-objs += power_supply_leds.o
9endif
10
11ifeq ($(CONFIG_POWER_SUPPLY_DEBUG),y)
12EXTRA_CFLAGS += -DDEBUG
13endif
14
15obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
diff --git a/drivers/power/power_supply.h b/drivers/power/power_supply.h
new file mode 100644
index 00000000000..a9880d468ee
--- /dev/null
+++ b/drivers/power/power_supply.h
@@ -0,0 +1,42 @@
1/*
2 * Functions private to power supply class
3 *
4 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
5 * Copyright © 2004 Szabolcs Gyurko
6 * Copyright © 2003 Ian Molton <spyro@f2s.com>
7 *
8 * Modified: 2004, Oct Szabolcs Gyurko
9 *
10 * You may use this code as per GPL version 2
11 */
12
13#ifdef CONFIG_SYSFS
14
15extern int power_supply_create_attrs(struct power_supply *psy);
16extern void power_supply_remove_attrs(struct power_supply *psy);
17extern int power_supply_uevent(struct device *dev, char **envp, int num_envp,
18 char *buffer, int buffer_size);
19
20#else
21
22static inline int power_supply_create_attrs(struct power_supply *psy)
23{ return 0; }
24static inline void power_supply_remove_attrs(struct power_supply *psy) {}
25#define power_supply_uevent NULL
26
27#endif /* CONFIG_SYSFS */
28
29#ifdef CONFIG_LEDS_TRIGGERS
30
31extern void power_supply_update_leds(struct power_supply *psy);
32extern int power_supply_create_triggers(struct power_supply *psy);
33extern void power_supply_remove_triggers(struct power_supply *psy);
34
35#else
36
37static inline void power_supply_update_leds(struct power_supply *psy) {}
38static inline int power_supply_create_triggers(struct power_supply *psy)
39{ return 0; }
40static inline void power_supply_remove_triggers(struct power_supply *psy) {}
41
42#endif /* CONFIG_LEDS_TRIGGERS */
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
new file mode 100644
index 00000000000..e87ea515675
--- /dev/null
+++ b/drivers/power/power_supply_core.c
@@ -0,0 +1,168 @@
1/*
2 * Universal power supply monitor class
3 *
4 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
5 * Copyright © 2004 Szabolcs Gyurko
6 * Copyright © 2003 Ian Molton <spyro@f2s.com>
7 *
8 * Modified: 2004, Oct Szabolcs Gyurko
9 *
10 * You may use this code as per GPL version 2
11 */
12
13#include <linux/module.h>
14#include <linux/types.h>
15#include <linux/init.h>
16#include <linux/device.h>
17#include <linux/err.h>
18#include <linux/power_supply.h>
19#include "power_supply.h"
20
21struct class *power_supply_class;
22
23static void power_supply_changed_work(struct work_struct *work)
24{
25 struct power_supply *psy = container_of(work, struct power_supply,
26 changed_work);
27 int i;
28
29 dev_dbg(psy->dev, "%s\n", __FUNCTION__);
30
31 for (i = 0; i < psy->num_supplicants; i++) {
32 struct device *dev;
33
34 down(&power_supply_class->sem);
35 list_for_each_entry(dev, &power_supply_class->devices, node) {
36 struct power_supply *pst = dev_get_drvdata(dev);
37
38 if (!strcmp(psy->supplied_to[i], pst->name)) {
39 if (pst->external_power_changed)
40 pst->external_power_changed(pst);
41 }
42 }
43 up(&power_supply_class->sem);
44 }
45
46 power_supply_update_leds(psy);
47
48 kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
49
50 return;
51}
52
53void power_supply_changed(struct power_supply *psy)
54{
55 dev_dbg(psy->dev, "%s\n", __FUNCTION__);
56
57 schedule_work(&psy->changed_work);
58
59 return;
60}
61
62int power_supply_am_i_supplied(struct power_supply *psy)
63{
64 union power_supply_propval ret = {0,};
65 struct device *dev;
66
67 down(&power_supply_class->sem);
68 list_for_each_entry(dev, &power_supply_class->devices, node) {
69 struct power_supply *epsy = dev_get_drvdata(dev);
70 int i;
71
72 for (i = 0; i < epsy->num_supplicants; i++) {
73 if (!strcmp(epsy->supplied_to[i], psy->name)) {
74 if (epsy->get_property(epsy,
75 POWER_SUPPLY_PROP_ONLINE, &ret))
76 continue;
77 if (ret.intval)
78 goto out;
79 }
80 }
81 }
82out:
83 up(&power_supply_class->sem);
84
85 dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, ret.intval);
86
87 return ret.intval;
88}
89
90int power_supply_register(struct device *parent, struct power_supply *psy)
91{
92 int rc = 0;
93
94 psy->dev = device_create(power_supply_class, parent, 0,
95 "%s", psy->name);
96 if (IS_ERR(psy->dev)) {
97 rc = PTR_ERR(psy->dev);
98 goto dev_create_failed;
99 }
100
101 dev_set_drvdata(psy->dev, psy);
102
103 INIT_WORK(&psy->changed_work, power_supply_changed_work);
104
105 rc = power_supply_create_attrs(psy);
106 if (rc)
107 goto create_attrs_failed;
108
109 rc = power_supply_create_triggers(psy);
110 if (rc)
111 goto create_triggers_failed;
112
113 power_supply_changed(psy);
114
115 goto success;
116
117create_triggers_failed:
118 power_supply_remove_attrs(psy);
119create_attrs_failed:
120 device_unregister(psy->dev);
121dev_create_failed:
122success:
123 return rc;
124}
125
126void power_supply_unregister(struct power_supply *psy)
127{
128 flush_scheduled_work();
129 power_supply_remove_triggers(psy);
130 power_supply_remove_attrs(psy);
131 device_unregister(psy->dev);
132 return;
133}
134
135static int __init power_supply_class_init(void)
136{
137 power_supply_class = class_create(THIS_MODULE, "power_supply");
138
139 if (IS_ERR(power_supply_class))
140 return PTR_ERR(power_supply_class);
141
142 power_supply_class->dev_uevent = power_supply_uevent;
143
144 return 0;
145}
146
147static void __exit power_supply_class_exit(void)
148{
149 class_destroy(power_supply_class);
150 return;
151}
152
153EXPORT_SYMBOL_GPL(power_supply_changed);
154EXPORT_SYMBOL_GPL(power_supply_am_i_supplied);
155EXPORT_SYMBOL_GPL(power_supply_register);
156EXPORT_SYMBOL_GPL(power_supply_unregister);
157
158/* exported for the APM Power driver, APM emulation */
159EXPORT_SYMBOL_GPL(power_supply_class);
160
161subsys_initcall(power_supply_class_init);
162module_exit(power_supply_class_exit);
163
164MODULE_DESCRIPTION("Universal power supply monitor class");
165MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, "
166 "Szabolcs Gyurko, "
167 "Anton Vorontsov <cbou@mail.ru>");
168MODULE_LICENSE("GPL");
diff --git a/drivers/power/power_supply_leds.c b/drivers/power/power_supply_leds.c
new file mode 100644
index 00000000000..7232490bb59
--- /dev/null
+++ b/drivers/power/power_supply_leds.c
@@ -0,0 +1,176 @@
1/*
2 * LEDs triggers for power supply class
3 *
4 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
5 * Copyright © 2004 Szabolcs Gyurko
6 * Copyright © 2003 Ian Molton <spyro@f2s.com>
7 *
8 * Modified: 2004, Oct Szabolcs Gyurko
9 *
10 * You may use this code as per GPL version 2
11 */
12
13#include <linux/power_supply.h>
14
15/* Battery specific LEDs triggers. */
16
17static void power_supply_update_bat_leds(struct power_supply *psy)
18{
19 union power_supply_propval status;
20
21 if (psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status))
22 return;
23
24 dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, status.intval);
25
26 switch (status.intval) {
27 case POWER_SUPPLY_STATUS_FULL:
28 led_trigger_event(psy->charging_full_trig, LED_FULL);
29 led_trigger_event(psy->charging_trig, LED_OFF);
30 led_trigger_event(psy->full_trig, LED_FULL);
31 break;
32 case POWER_SUPPLY_STATUS_CHARGING:
33 led_trigger_event(psy->charging_full_trig, LED_FULL);
34 led_trigger_event(psy->charging_trig, LED_FULL);
35 led_trigger_event(psy->full_trig, LED_OFF);
36 break;
37 default:
38 led_trigger_event(psy->charging_full_trig, LED_OFF);
39 led_trigger_event(psy->charging_trig, LED_OFF);
40 led_trigger_event(psy->full_trig, LED_OFF);
41 break;
42 }
43
44 return;
45}
46
47static int power_supply_create_bat_triggers(struct power_supply *psy)
48{
49 int rc = 0;
50
51 psy->charging_full_trig_name = kmalloc(strlen(psy->name) +
52 sizeof("-charging-or-full"), GFP_KERNEL);
53 if (!psy->charging_full_trig_name)
54 goto charging_full_failed;
55
56 psy->charging_trig_name = kmalloc(strlen(psy->name) +
57 sizeof("-charging"), GFP_KERNEL);
58 if (!psy->charging_trig_name)
59 goto charging_failed;
60
61 psy->full_trig_name = kmalloc(strlen(psy->name) +
62 sizeof("-full"), GFP_KERNEL);
63 if (!psy->full_trig_name)
64 goto full_failed;
65
66 strcpy(psy->charging_full_trig_name, psy->name);
67 strcat(psy->charging_full_trig_name, "-charging-or-full");
68 strcpy(psy->charging_trig_name, psy->name);
69 strcat(psy->charging_trig_name, "-charging");
70 strcpy(psy->full_trig_name, psy->name);
71 strcat(psy->full_trig_name, "-full");
72
73 led_trigger_register_simple(psy->charging_full_trig_name,
74 &psy->charging_full_trig);
75 led_trigger_register_simple(psy->charging_trig_name,
76 &psy->charging_trig);
77 led_trigger_register_simple(psy->full_trig_name,
78 &psy->full_trig);
79
80 goto success;
81
82full_failed:
83 kfree(psy->charging_trig_name);
84charging_failed:
85 kfree(psy->charging_full_trig_name);
86charging_full_failed:
87 rc = -ENOMEM;
88success:
89 return rc;
90}
91
92static void power_supply_remove_bat_triggers(struct power_supply *psy)
93{
94 led_trigger_unregister_simple(psy->charging_full_trig);
95 led_trigger_unregister_simple(psy->charging_trig);
96 led_trigger_unregister_simple(psy->full_trig);
97 kfree(psy->full_trig_name);
98 kfree(psy->charging_trig_name);
99 kfree(psy->charging_full_trig_name);
100 return;
101}
102
103/* Generated power specific LEDs triggers. */
104
105static void power_supply_update_gen_leds(struct power_supply *psy)
106{
107 union power_supply_propval online;
108
109 if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online))
110 return;
111
112 dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, online.intval);
113
114 if (online.intval)
115 led_trigger_event(psy->online_trig, LED_FULL);
116 else
117 led_trigger_event(psy->online_trig, LED_OFF);
118
119 return;
120}
121
122static int power_supply_create_gen_triggers(struct power_supply *psy)
123{
124 int rc = 0;
125
126 psy->online_trig_name = kmalloc(strlen(psy->name) + sizeof("-online"),
127 GFP_KERNEL);
128 if (!psy->online_trig_name)
129 goto online_failed;
130
131 strcpy(psy->online_trig_name, psy->name);
132 strcat(psy->online_trig_name, "-online");
133
134 led_trigger_register_simple(psy->online_trig_name, &psy->online_trig);
135
136 goto success;
137
138online_failed:
139 rc = -ENOMEM;
140success:
141 return rc;
142}
143
144static void power_supply_remove_gen_triggers(struct power_supply *psy)
145{
146 led_trigger_unregister_simple(psy->online_trig);
147 kfree(psy->online_trig_name);
148 return;
149}
150
151/* Choice what triggers to create&update. */
152
153void power_supply_update_leds(struct power_supply *psy)
154{
155 if (psy->type == POWER_SUPPLY_TYPE_BATTERY)
156 power_supply_update_bat_leds(psy);
157 else
158 power_supply_update_gen_leds(psy);
159 return;
160}
161
162int power_supply_create_triggers(struct power_supply *psy)
163{
164 if (psy->type == POWER_SUPPLY_TYPE_BATTERY)
165 return power_supply_create_bat_triggers(psy);
166 return power_supply_create_gen_triggers(psy);
167}
168
169void power_supply_remove_triggers(struct power_supply *psy)
170{
171 if (psy->type == POWER_SUPPLY_TYPE_BATTERY)
172 power_supply_remove_bat_triggers(psy);
173 else
174 power_supply_remove_gen_triggers(psy);
175 return;
176}
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
new file mode 100644
index 00000000000..c07d4258d34
--- /dev/null
+++ b/drivers/power/power_supply_sysfs.c
@@ -0,0 +1,299 @@
1/*
2 * Sysfs interface for the universal power supply monitor class
3 *
4 * Copyright © 2007 David Woodhouse <dwmw2@infradead.org>
5 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
6 * Copyright © 2004 Szabolcs Gyurko
7 * Copyright © 2003 Ian Molton <spyro@f2s.com>
8 *
9 * Modified: 2004, Oct Szabolcs Gyurko
10 *
11 * You may use this code as per GPL version 2
12 */
13
14#include <linux/ctype.h>
15#include <linux/power_supply.h>
16
17/*
18 * This is because the name "current" breaks the device attr macro.
19 * The "current" word resolves to "(get_current())" so instead of
20 * "current" "(get_current())" appears in the sysfs.
21 *
22 * The source of this definition is the device.h which calls __ATTR
23 * macro in sysfs.h which calls the __stringify macro.
24 *
25 * Only modification that the name is not tried to be resolved
26 * (as a macro let's say).
27 */
28
29#define POWER_SUPPLY_ATTR(_name) \
30{ \
31 .attr = { .name = #_name, .mode = 0444, .owner = THIS_MODULE }, \
32 .show = power_supply_show_property, \
33 .store = NULL, \
34}
35
36static struct device_attribute power_supply_attrs[];
37
38static ssize_t power_supply_show_property(struct device *dev,
39 struct device_attribute *attr,
40 char *buf) {
41 static char *status_text[] = {
42 "Unknown", "Charging", "Discharging", "Not charging", "Full"
43 };
44 static char *health_text[] = {
45 "Unknown", "Good", "Overheat", "Dead", "Over voltage",
46 "Unspecified failure"
47 };
48 static char *technology_text[] = {
49 "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd"
50 };
51 static char *capacity_level_text[] = {
52 "Unknown", "Critical", "Low", "Normal", "High", "Full"
53 };
54 ssize_t ret;
55 struct power_supply *psy = dev_get_drvdata(dev);
56 const ptrdiff_t off = attr - power_supply_attrs;
57 union power_supply_propval value;
58
59 ret = psy->get_property(psy, off, &value);
60
61 if (ret < 0) {
62 if (ret != -ENODEV)
63 dev_err(dev, "driver failed to report `%s' property\n",
64 attr->attr.name);
65 return ret;
66 }
67
68 if (off == POWER_SUPPLY_PROP_STATUS)
69 return sprintf(buf, "%s\n", status_text[value.intval]);
70 else if (off == POWER_SUPPLY_PROP_HEALTH)
71 return sprintf(buf, "%s\n", health_text[value.intval]);
72 else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)
73 return sprintf(buf, "%s\n", technology_text[value.intval]);
74 else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
75 return sprintf(buf, "%s\n",
76 capacity_level_text[value.intval]);
77 else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
78 return sprintf(buf, "%s\n", value.strval);
79
80 return sprintf(buf, "%d\n", value.intval);
81}
82
83/* Must be in the same order as POWER_SUPPLY_PROP_* */
84static struct device_attribute power_supply_attrs[] = {
85 /* Properties of type `int' */
86 POWER_SUPPLY_ATTR(status),
87 POWER_SUPPLY_ATTR(health),
88 POWER_SUPPLY_ATTR(present),
89 POWER_SUPPLY_ATTR(online),
90 POWER_SUPPLY_ATTR(technology),
91 POWER_SUPPLY_ATTR(voltage_max_design),
92 POWER_SUPPLY_ATTR(voltage_min_design),
93 POWER_SUPPLY_ATTR(voltage_now),
94 POWER_SUPPLY_ATTR(voltage_avg),
95 POWER_SUPPLY_ATTR(current_now),
96 POWER_SUPPLY_ATTR(current_avg),
97 POWER_SUPPLY_ATTR(charge_full_design),
98 POWER_SUPPLY_ATTR(charge_empty_design),
99 POWER_SUPPLY_ATTR(charge_full),
100 POWER_SUPPLY_ATTR(charge_empty),
101 POWER_SUPPLY_ATTR(charge_now),
102 POWER_SUPPLY_ATTR(charge_avg),
103 POWER_SUPPLY_ATTR(energy_full_design),
104 POWER_SUPPLY_ATTR(energy_empty_design),
105 POWER_SUPPLY_ATTR(energy_full),
106 POWER_SUPPLY_ATTR(energy_empty),
107 POWER_SUPPLY_ATTR(energy_now),
108 POWER_SUPPLY_ATTR(energy_avg),
109 POWER_SUPPLY_ATTR(capacity),
110 POWER_SUPPLY_ATTR(capacity_level),
111 POWER_SUPPLY_ATTR(temp),
112 POWER_SUPPLY_ATTR(temp_ambient),
113 POWER_SUPPLY_ATTR(time_to_empty_now),
114 POWER_SUPPLY_ATTR(time_to_empty_avg),
115 POWER_SUPPLY_ATTR(time_to_full_now),
116 POWER_SUPPLY_ATTR(time_to_full_avg),
117 /* Properties of type `const char *' */
118 POWER_SUPPLY_ATTR(model_name),
119 POWER_SUPPLY_ATTR(manufacturer),
120};
121
122static ssize_t power_supply_show_static_attrs(struct device *dev,
123 struct device_attribute *attr,
124 char *buf) {
125 static char *type_text[] = { "Battery", "UPS", "Mains", "USB" };
126 struct power_supply *psy = dev_get_drvdata(dev);
127
128 return sprintf(buf, "%s\n", type_text[psy->type]);
129}
130
131static struct device_attribute power_supply_static_attrs[] = {
132 __ATTR(type, 0444, power_supply_show_static_attrs, NULL),
133};
134
135int power_supply_create_attrs(struct power_supply *psy)
136{
137 int rc = 0;
138 int i, j;
139
140 for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) {
141 rc = device_create_file(psy->dev,
142 &power_supply_static_attrs[i]);
143 if (rc)
144 goto statics_failed;
145 }
146
147 for (j = 0; j < psy->num_properties; j++) {
148 rc = device_create_file(psy->dev,
149 &power_supply_attrs[psy->properties[j]]);
150 if (rc)
151 goto dynamics_failed;
152 }
153
154 goto succeed;
155
156dynamics_failed:
157 while (j--)
158 device_remove_file(psy->dev,
159 &power_supply_attrs[psy->properties[j]]);
160statics_failed:
161 while (i--)
162 device_remove_file(psy->dev,
163 &power_supply_static_attrs[psy->properties[i]]);
164succeed:
165 return rc;
166}
167
168void power_supply_remove_attrs(struct power_supply *psy)
169{
170 int i;
171
172 for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++)
173 device_remove_file(psy->dev,
174 &power_supply_static_attrs[i]);
175
176 for (i = 0; i < psy->num_properties; i++)
177 device_remove_file(psy->dev,
178 &power_supply_attrs[psy->properties[i]]);
179
180 return;
181}
182
183static char *kstruprdup(const char *str, gfp_t gfp)
184{
185 char *ret, *ustr;
186
187 ustr = ret = kmalloc(strlen(str) + 1, gfp);
188
189 if (!ret)
190 return NULL;
191
192 while (*str)
193 *ustr++ = toupper(*str++);
194
195 *ustr = 0;
196
197 return ret;
198}
199
200int power_supply_uevent(struct device *dev, char **envp, int num_envp,
201 char *buffer, int buffer_size)
202{
203 struct power_supply *psy = dev_get_drvdata(dev);
204 int i = 0, length = 0, ret = 0, j;
205 char *prop_buf;
206 char *attrname;
207
208 dev_dbg(dev, "uevent\n");
209
210 if (!psy) {
211 dev_dbg(dev, "No power supply yet\n");
212 return ret;
213 }
214
215 dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name);
216
217 ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
218 &length, "POWER_SUPPLY_NAME=%s", psy->name);
219 if (ret)
220 return ret;
221
222 prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
223 if (!prop_buf)
224 return -ENOMEM;
225
226 for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) {
227 struct device_attribute *attr;
228 char *line;
229
230 attr = &power_supply_static_attrs[j];
231
232 ret = power_supply_show_static_attrs(dev, attr, prop_buf);
233 if (ret < 0)
234 goto out;
235
236 line = strchr(prop_buf, '\n');
237 if (line)
238 *line = 0;
239
240 attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
241 if (!attrname) {
242 ret = -ENOMEM;
243 goto out;
244 }
245
246 dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf);
247
248 ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
249 &length, "POWER_SUPPLY_%s=%s",
250 attrname, prop_buf);
251 kfree(attrname);
252 if (ret)
253 goto out;
254 }
255
256 dev_dbg(dev, "%zd dynamic props\n", psy->num_properties);
257
258 for (j = 0; j < psy->num_properties; j++) {
259 struct device_attribute *attr;
260 char *line;
261
262 attr = &power_supply_attrs[psy->properties[j]];
263
264 ret = power_supply_show_property(dev, attr, prop_buf);
265 if (ret == -ENODEV) {
266 /* When a battery is absent, we expect -ENODEV. Don't abort;
267 send the uevent with at least the the PRESENT=0 property */
268 ret = 0;
269 continue;
270 }
271
272 if (ret < 0)
273 goto out;
274
275 line = strchr(prop_buf, '\n');
276 if (line)
277 *line = 0;
278
279 attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
280 if (!attrname) {
281 ret = -ENOMEM;
282 goto out;
283 }
284
285 dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);
286
287 ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
288 &length, "POWER_SUPPLY_%s=%s",
289 attrname, prop_buf);
290 kfree(attrname);
291 if (ret)
292 goto out;
293 }
294
295out:
296 free_page((unsigned long)prop_buf);
297
298 return ret;
299}
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
new file mode 100644
index 00000000000..606c0957997
--- /dev/null
+++ b/include/linux/power_supply.h
@@ -0,0 +1,180 @@
1/*
2 * Universal power supply monitor class
3 *
4 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
5 * Copyright © 2004 Szabolcs Gyurko
6 * Copyright © 2003 Ian Molton <spyro@f2s.com>
7 *
8 * Modified: 2004, Oct Szabolcs Gyurko
9 *
10 * You may use this code as per GPL version 2
11 */
12
13#ifndef __LINUX_POWER_SUPPLY_H__
14#define __LINUX_POWER_SUPPLY_H__
15
16#include <linux/device.h>
17#include <linux/workqueue.h>
18#include <linux/leds.h>
19
20/*
21 * All voltages, currents, charges, energies, time and temperatures in uV,
22 * µA, µAh, µWh, seconds and tenths of degree Celsius unless otherwise
23 * stated. It's driver's job to convert its raw values to units in which
24 * this class operates.
25 */
26
27/*
28 * For systems where the charger determines the maximum battery capacity
29 * the min and max fields should be used to present these values to user
30 * space. Unused/unknown fields will not appear in sysfs.
31 */
32
33enum {
34 POWER_SUPPLY_STATUS_UNKNOWN = 0,
35 POWER_SUPPLY_STATUS_CHARGING,
36 POWER_SUPPLY_STATUS_DISCHARGING,
37 POWER_SUPPLY_STATUS_NOT_CHARGING,
38 POWER_SUPPLY_STATUS_FULL,
39};
40
41enum {
42 POWER_SUPPLY_HEALTH_UNKNOWN = 0,
43 POWER_SUPPLY_HEALTH_GOOD,
44 POWER_SUPPLY_HEALTH_OVERHEAT,
45 POWER_SUPPLY_HEALTH_DEAD,
46 POWER_SUPPLY_HEALTH_OVERVOLTAGE,
47 POWER_SUPPLY_HEALTH_UNSPEC_FAILURE,
48};
49
50enum {
51 POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0,
52 POWER_SUPPLY_TECHNOLOGY_NiMH,
53 POWER_SUPPLY_TECHNOLOGY_LION,
54 POWER_SUPPLY_TECHNOLOGY_LIPO,
55 POWER_SUPPLY_TECHNOLOGY_LiFe,
56 POWER_SUPPLY_TECHNOLOGY_NiCd,
57};
58
59enum {
60 POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0,
61 POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL,
62 POWER_SUPPLY_CAPACITY_LEVEL_LOW,
63 POWER_SUPPLY_CAPACITY_LEVEL_NORMAL,
64 POWER_SUPPLY_CAPACITY_LEVEL_HIGH,
65 POWER_SUPPLY_CAPACITY_LEVEL_FULL,
66};
67
68enum power_supply_property {
69 /* Properties of type `int' */
70 POWER_SUPPLY_PROP_STATUS = 0,
71 POWER_SUPPLY_PROP_HEALTH,
72 POWER_SUPPLY_PROP_PRESENT,
73 POWER_SUPPLY_PROP_ONLINE,
74 POWER_SUPPLY_PROP_TECHNOLOGY,
75 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
76 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
77 POWER_SUPPLY_PROP_VOLTAGE_NOW,
78 POWER_SUPPLY_PROP_VOLTAGE_AVG,
79 POWER_SUPPLY_PROP_CURRENT_NOW,
80 POWER_SUPPLY_PROP_CURRENT_AVG,
81 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
82 POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
83 POWER_SUPPLY_PROP_CHARGE_FULL,
84 POWER_SUPPLY_PROP_CHARGE_EMPTY,
85 POWER_SUPPLY_PROP_CHARGE_NOW,
86 POWER_SUPPLY_PROP_CHARGE_AVG,
87 POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
88 POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
89 POWER_SUPPLY_PROP_ENERGY_FULL,
90 POWER_SUPPLY_PROP_ENERGY_EMPTY,
91 POWER_SUPPLY_PROP_ENERGY_NOW,
92 POWER_SUPPLY_PROP_ENERGY_AVG,
93 POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
94 POWER_SUPPLY_PROP_CAPACITY_LEVEL,
95 POWER_SUPPLY_PROP_TEMP,
96 POWER_SUPPLY_PROP_TEMP_AMBIENT,
97 POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
98 POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
99 POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
100 POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
101 /* Properties of type `const char *' */
102 POWER_SUPPLY_PROP_MODEL_NAME,
103 POWER_SUPPLY_PROP_MANUFACTURER,
104};
105
106enum power_supply_type {
107 POWER_SUPPLY_TYPE_BATTERY = 0,
108 POWER_SUPPLY_TYPE_UPS,
109 POWER_SUPPLY_TYPE_MAINS,
110 POWER_SUPPLY_TYPE_USB,
111};
112
113union power_supply_propval {
114 int intval;
115 const char *strval;
116};
117
118struct power_supply {
119 const char *name;
120 enum power_supply_type type;
121 enum power_supply_property *properties;
122 size_t num_properties;
123
124 char **supplied_to;
125 size_t num_supplicants;
126
127 int (*get_property)(struct power_supply *psy,
128 enum power_supply_property psp,
129 union power_supply_propval *val);
130 void (*external_power_changed)(struct power_supply *psy);
131
132 /* For APM emulation, think legacy userspace. */
133 int use_for_apm;
134
135 /* private */
136 struct device *dev;
137 struct work_struct changed_work;
138
139#ifdef CONFIG_LEDS_TRIGGERS
140 struct led_trigger *charging_full_trig;
141 char *charging_full_trig_name;
142 struct led_trigger *charging_trig;
143 char *charging_trig_name;
144 struct led_trigger *full_trig;
145 char *full_trig_name;
146 struct led_trigger *online_trig;
147 char *online_trig_name;
148#endif
149};
150
151/*
152 * This is recommended structure to specify static power supply parameters.
153 * Generic one, parametrizable for different power supplies. Power supply
154 * class itself does not use it, but that's what implementing most platform
155 * drivers, should try reuse for consistency.
156 */
157
158struct power_supply_info {
159 const char *name;
160 int technology;
161 int voltage_max_design;
162 int voltage_min_design;
163 int charge_full_design;
164 int charge_empty_design;
165 int energy_full_design;
166 int energy_empty_design;
167 int use_for_apm;
168};
169
170extern void power_supply_changed(struct power_supply *psy);
171extern int power_supply_am_i_supplied(struct power_supply *psy);
172
173extern int power_supply_register(struct device *parent,
174 struct power_supply *psy);
175extern void power_supply_unregister(struct power_supply *psy);
176
177/* For APM emulation, think legacy userspace. */
178extern struct class *power_supply_class;
179
180#endif /* __LINUX_POWER_SUPPLY_H__ */