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