blob: 3928e7cdddc13331c5176491df1f7891896cc4fb [file] [log] [blame]
Anton Vorontsov3788ec92007-05-04 00:43:24 +04001/*
2 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
3 * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
4 *
5 * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
6 *
7 * Use consistent with the GNU GPL is permitted,
8 * provided that this copyright notice is
9 * preserved in its entirety in all copies and derived works.
10 */
11
12#include <linux/module.h>
13#include <linux/power_supply.h>
14#include <linux/apm-emulation.h>
15
16#define PSY_PROP(psy, prop, val) psy->get_property(psy, \
17 POWER_SUPPLY_PROP_##prop, val)
18
19#define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \
20 prop, val)
21
22#define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
23
24static struct power_supply *main_battery;
25
26static void find_main_battery(void)
27{
28 struct device *dev;
Anton Vorontsovd3853762007-10-05 01:05:00 +040029 struct power_supply *bat = NULL;
30 struct power_supply *max_charge_bat = NULL;
31 struct power_supply *max_energy_bat = NULL;
Anton Vorontsov3788ec92007-05-04 00:43:24 +040032 union power_supply_propval full;
33 int max_charge = 0;
Anton Vorontsovd3853762007-10-05 01:05:00 +040034 int max_energy = 0;
Anton Vorontsov3788ec92007-05-04 00:43:24 +040035
36 main_battery = NULL;
Anton Vorontsovd3853762007-10-05 01:05:00 +040037
Anton Vorontsov3788ec92007-05-04 00:43:24 +040038 list_for_each_entry(dev, &power_supply_class->devices, node) {
39 bat = dev_get_drvdata(dev);
Anton Vorontsovd3853762007-10-05 01:05:00 +040040
41 if (bat->use_for_apm) {
42 /* nice, we explicitly asked to report this battery. */
43 main_battery = bat;
44 return;
Anton Vorontsov3788ec92007-05-04 00:43:24 +040045 }
46
Anton Vorontsovd3853762007-10-05 01:05:00 +040047 if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full) ||
48 !PSY_PROP(bat, CHARGE_FULL, &full)) {
49 if (full.intval > max_charge) {
50 max_charge_bat = bat;
51 max_charge = full.intval;
52 }
53 } else if (!PSY_PROP(bat, ENERGY_FULL_DESIGN, &full) ||
54 !PSY_PROP(bat, ENERGY_FULL, &full)) {
55 if (full.intval > max_energy) {
56 max_energy_bat = bat;
57 max_energy = full.intval;
58 }
59 }
Anton Vorontsov3788ec92007-05-04 00:43:24 +040060 }
Anton Vorontsovd3853762007-10-05 01:05:00 +040061
62 if ((max_energy_bat && max_charge_bat) &&
63 (max_energy_bat != max_charge_bat)) {
64 /* try guess battery with more capacity */
65 if (!PSY_PROP(max_charge_bat, VOLTAGE_MAX_DESIGN, &full)) {
66 if (max_energy > max_charge * full.intval)
67 main_battery = max_energy_bat;
68 else
69 main_battery = max_charge_bat;
70 } else if (!PSY_PROP(max_energy_bat, VOLTAGE_MAX_DESIGN,
71 &full)) {
72 if (max_charge > max_energy / full.intval)
73 main_battery = max_charge_bat;
74 else
75 main_battery = max_energy_bat;
76 } else {
77 /* give up, choice any */
78 main_battery = max_energy_bat;
79 }
80 } else if (max_charge_bat) {
81 main_battery = max_charge_bat;
82 } else if (max_energy_bat) {
83 main_battery = max_energy_bat;
84 } else {
85 /* give up, try the last if any */
86 main_battery = bat;
87 }
Anton Vorontsov3788ec92007-05-04 00:43:24 +040088}
89
90static int calculate_time(int status)
91{
92 union power_supply_propval charge_full, charge_empty;
93 union power_supply_propval charge, I;
94
95 if (MPSY_PROP(CHARGE_FULL, &charge_full)) {
96 /* if battery can't report this property, use design value */
97 if (MPSY_PROP(CHARGE_FULL_DESIGN, &charge_full))
98 return -1;
99 }
100
101 if (MPSY_PROP(CHARGE_EMPTY, &charge_empty)) {
102 /* if battery can't report this property, use design value */
103 if (MPSY_PROP(CHARGE_EMPTY_DESIGN, &charge_empty))
104 charge_empty.intval = 0;
105 }
106
107 if (MPSY_PROP(CHARGE_AVG, &charge)) {
108 /* if battery can't report average value, use momentary */
109 if (MPSY_PROP(CHARGE_NOW, &charge))
110 return -1;
111 }
112
113 if (MPSY_PROP(CURRENT_AVG, &I)) {
114 /* if battery can't report average value, use momentary */
115 if (MPSY_PROP(CURRENT_NOW, &I))
116 return -1;
117 }
118
119 if (status == POWER_SUPPLY_STATUS_CHARGING)
120 return ((charge.intval - charge_full.intval) * 60L) /
121 I.intval;
122 else
123 return -((charge.intval - charge_empty.intval) * 60L) /
124 I.intval;
125}
126
127static int calculate_capacity(int using_charge)
128{
129 enum power_supply_property full_prop, empty_prop;
130 enum power_supply_property full_design_prop, empty_design_prop;
131 enum power_supply_property now_prop, avg_prop;
132 union power_supply_propval empty, full, cur;
133 int ret;
134
135 if (using_charge) {
136 full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
137 empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
138 full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
139 empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
140 now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
141 avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
142 } else {
143 full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
144 empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
145 full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
146 empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
147 now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
148 avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
149 }
150
151 if (_MPSY_PROP(full_prop, &full)) {
152 /* if battery can't report this property, use design value */
153 if (_MPSY_PROP(full_design_prop, &full))
154 return -1;
155 }
156
157 if (_MPSY_PROP(avg_prop, &cur)) {
158 /* if battery can't report average value, use momentary */
159 if (_MPSY_PROP(now_prop, &cur))
160 return -1;
161 }
162
163 if (_MPSY_PROP(empty_prop, &empty)) {
164 /* if battery can't report this property, use design value */
165 if (_MPSY_PROP(empty_design_prop, &empty))
166 empty.intval = 0;
167 }
168
169 if (full.intval - empty.intval)
170 ret = ((cur.intval - empty.intval) * 100L) /
171 (full.intval - empty.intval);
172 else
173 return -1;
174
175 if (ret > 100)
176 return 100;
177 else if (ret < 0)
178 return 0;
179
180 return ret;
181}
182
183static void apm_battery_apm_get_power_status(struct apm_power_info *info)
184{
185 union power_supply_propval status;
186 union power_supply_propval capacity, time_to_full, time_to_empty;
187
188 down(&power_supply_class->sem);
189 find_main_battery();
190 if (!main_battery) {
191 up(&power_supply_class->sem);
192 return;
193 }
194
195 /* status */
196
197 if (MPSY_PROP(STATUS, &status))
198 status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
199
200 /* ac line status */
201
202 if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
203 (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
204 (status.intval == POWER_SUPPLY_STATUS_FULL))
205 info->ac_line_status = APM_AC_ONLINE;
206 else
207 info->ac_line_status = APM_AC_OFFLINE;
208
209 /* battery life (i.e. capacity, in percents) */
210
211 if (MPSY_PROP(CAPACITY, &capacity) == 0) {
212 info->battery_life = capacity.intval;
213 } else {
214 /* try calculate using energy */
215 info->battery_life = calculate_capacity(0);
216 /* if failed try calculate using charge instead */
217 if (info->battery_life == -1)
218 info->battery_life = calculate_capacity(1);
219 }
220
221 /* charging status */
222
223 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
224 info->battery_status = APM_BATTERY_STATUS_CHARGING;
225 } else {
226 if (info->battery_life > 50)
227 info->battery_status = APM_BATTERY_STATUS_HIGH;
228 else if (info->battery_life > 5)
229 info->battery_status = APM_BATTERY_STATUS_LOW;
230 else
231 info->battery_status = APM_BATTERY_STATUS_CRITICAL;
232 }
233 info->battery_flag = info->battery_status;
234
235 /* time */
236
237 info->units = APM_UNITS_MINS;
238
239 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
Anton Vorontsovcd1ebcc2007-10-05 01:04:59 +0400240 if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
241 !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
242 info->time = time_to_full.intval / 60;
243 else
244 info->time = calculate_time(status.intval);
Anton Vorontsov3788ec92007-05-04 00:43:24 +0400245 } else {
Anton Vorontsovcd1ebcc2007-10-05 01:04:59 +0400246 if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
247 !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
248 info->time = time_to_empty.intval / 60;
249 else
250 info->time = calculate_time(status.intval);
Anton Vorontsov3788ec92007-05-04 00:43:24 +0400251 }
252
253 up(&power_supply_class->sem);
Anton Vorontsov3788ec92007-05-04 00:43:24 +0400254}
255
256static int __init apm_battery_init(void)
257{
258 printk(KERN_INFO "APM Battery Driver\n");
259
260 apm_get_power_status = apm_battery_apm_get_power_status;
261 return 0;
262}
263
264static void __exit apm_battery_exit(void)
265{
266 apm_get_power_status = NULL;
Anton Vorontsov3788ec92007-05-04 00:43:24 +0400267}
268
269module_init(apm_battery_init);
270module_exit(apm_battery_exit);
271
272MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
273MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
274MODULE_LICENSE("GPL");