blob: 042bd950d036109f0a9cb44ad25b661d0f1896f0 [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;
29 struct power_supply *bat, *batm;
30 union power_supply_propval full;
31 int max_charge = 0;
32
33 main_battery = NULL;
34 batm = NULL;
35 list_for_each_entry(dev, &power_supply_class->devices, node) {
36 bat = dev_get_drvdata(dev);
37 /* If none of battery devices cantains 'use_for_apm' flag,
38 choice one with maximum design charge */
39 if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full)) {
40 if (full.intval > max_charge) {
41 batm = bat;
42 max_charge = full.intval;
43 }
44 }
45
46 if (bat->use_for_apm)
47 main_battery = bat;
48 }
49 if (!main_battery)
50 main_battery = batm;
51
52 return;
53}
54
55static int calculate_time(int status)
56{
57 union power_supply_propval charge_full, charge_empty;
58 union power_supply_propval charge, I;
59
60 if (MPSY_PROP(CHARGE_FULL, &charge_full)) {
61 /* if battery can't report this property, use design value */
62 if (MPSY_PROP(CHARGE_FULL_DESIGN, &charge_full))
63 return -1;
64 }
65
66 if (MPSY_PROP(CHARGE_EMPTY, &charge_empty)) {
67 /* if battery can't report this property, use design value */
68 if (MPSY_PROP(CHARGE_EMPTY_DESIGN, &charge_empty))
69 charge_empty.intval = 0;
70 }
71
72 if (MPSY_PROP(CHARGE_AVG, &charge)) {
73 /* if battery can't report average value, use momentary */
74 if (MPSY_PROP(CHARGE_NOW, &charge))
75 return -1;
76 }
77
78 if (MPSY_PROP(CURRENT_AVG, &I)) {
79 /* if battery can't report average value, use momentary */
80 if (MPSY_PROP(CURRENT_NOW, &I))
81 return -1;
82 }
83
84 if (status == POWER_SUPPLY_STATUS_CHARGING)
85 return ((charge.intval - charge_full.intval) * 60L) /
86 I.intval;
87 else
88 return -((charge.intval - charge_empty.intval) * 60L) /
89 I.intval;
90}
91
92static int calculate_capacity(int using_charge)
93{
94 enum power_supply_property full_prop, empty_prop;
95 enum power_supply_property full_design_prop, empty_design_prop;
96 enum power_supply_property now_prop, avg_prop;
97 union power_supply_propval empty, full, cur;
98 int ret;
99
100 if (using_charge) {
101 full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
102 empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
103 full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
104 empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
105 now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
106 avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
107 } else {
108 full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
109 empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
110 full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
111 empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
112 now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
113 avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
114 }
115
116 if (_MPSY_PROP(full_prop, &full)) {
117 /* if battery can't report this property, use design value */
118 if (_MPSY_PROP(full_design_prop, &full))
119 return -1;
120 }
121
122 if (_MPSY_PROP(avg_prop, &cur)) {
123 /* if battery can't report average value, use momentary */
124 if (_MPSY_PROP(now_prop, &cur))
125 return -1;
126 }
127
128 if (_MPSY_PROP(empty_prop, &empty)) {
129 /* if battery can't report this property, use design value */
130 if (_MPSY_PROP(empty_design_prop, &empty))
131 empty.intval = 0;
132 }
133
134 if (full.intval - empty.intval)
135 ret = ((cur.intval - empty.intval) * 100L) /
136 (full.intval - empty.intval);
137 else
138 return -1;
139
140 if (ret > 100)
141 return 100;
142 else if (ret < 0)
143 return 0;
144
145 return ret;
146}
147
148static void apm_battery_apm_get_power_status(struct apm_power_info *info)
149{
150 union power_supply_propval status;
151 union power_supply_propval capacity, time_to_full, time_to_empty;
152
153 down(&power_supply_class->sem);
154 find_main_battery();
155 if (!main_battery) {
156 up(&power_supply_class->sem);
157 return;
158 }
159
160 /* status */
161
162 if (MPSY_PROP(STATUS, &status))
163 status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
164
165 /* ac line status */
166
167 if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
168 (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
169 (status.intval == POWER_SUPPLY_STATUS_FULL))
170 info->ac_line_status = APM_AC_ONLINE;
171 else
172 info->ac_line_status = APM_AC_OFFLINE;
173
174 /* battery life (i.e. capacity, in percents) */
175
176 if (MPSY_PROP(CAPACITY, &capacity) == 0) {
177 info->battery_life = capacity.intval;
178 } else {
179 /* try calculate using energy */
180 info->battery_life = calculate_capacity(0);
181 /* if failed try calculate using charge instead */
182 if (info->battery_life == -1)
183 info->battery_life = calculate_capacity(1);
184 }
185
186 /* charging status */
187
188 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
189 info->battery_status = APM_BATTERY_STATUS_CHARGING;
190 } else {
191 if (info->battery_life > 50)
192 info->battery_status = APM_BATTERY_STATUS_HIGH;
193 else if (info->battery_life > 5)
194 info->battery_status = APM_BATTERY_STATUS_LOW;
195 else
196 info->battery_status = APM_BATTERY_STATUS_CRITICAL;
197 }
198 info->battery_flag = info->battery_status;
199
200 /* time */
201
202 info->units = APM_UNITS_MINS;
203
204 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
205 if (MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full)) {
206 if (MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
207 info->time = calculate_time(status.intval);
208 else
209 info->time = time_to_full.intval / 60;
210 }
211 } else {
212 if (MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty)) {
213 if (MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
214 info->time = calculate_time(status.intval);
215 else
216 info->time = time_to_empty.intval / 60;
217 }
218 }
219
220 up(&power_supply_class->sem);
221 return;
222}
223
224static int __init apm_battery_init(void)
225{
226 printk(KERN_INFO "APM Battery Driver\n");
227
228 apm_get_power_status = apm_battery_apm_get_power_status;
229 return 0;
230}
231
232static void __exit apm_battery_exit(void)
233{
234 apm_get_power_status = NULL;
235 return;
236}
237
238module_init(apm_battery_init);
239module_exit(apm_battery_exit);
240
241MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
242MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
243MODULE_LICENSE("GPL");