Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
apm_power.c
Go to the documentation of this file.
1 /*
2  * Copyright © 2007 Anton Vorontsov <[email protected]>
3  * Copyright © 2007 Eugeny Boger <[email protected]>
4  *
5  * Author: Eugeny Boger <[email protected]>
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/device.h>
14 #include <linux/power_supply.h>
15 #include <linux/apm-emulation.h>
16 
17 
18 #define PSY_PROP(psy, prop, val) (psy->get_property(psy, \
19  POWER_SUPPLY_PROP_##prop, val))
20 
21 #define _MPSY_PROP(prop, val) (main_battery->get_property(main_battery, \
22  prop, val))
23 
24 #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
25 
26 static DEFINE_MUTEX(apm_mutex);
27 static struct power_supply *main_battery;
28 
29 enum apm_source {
33 };
34 
36  struct power_supply *main;
37  struct power_supply *bat;
43 };
44 
45 static int __find_main_battery(struct device *dev, void *data)
46 {
47  struct find_bat_param *bp = (struct find_bat_param *)data;
48 
49  bp->bat = dev_get_drvdata(dev);
50 
51  if (bp->bat->use_for_apm) {
52  /* nice, we explicitly asked to report this battery. */
53  bp->main = bp->bat;
54  return 1;
55  }
56 
57  if (!PSY_PROP(bp->bat, CHARGE_FULL_DESIGN, &bp->full) ||
58  !PSY_PROP(bp->bat, CHARGE_FULL, &bp->full)) {
59  if (bp->full.intval > bp->max_charge) {
60  bp->max_charge_bat = bp->bat;
61  bp->max_charge = bp->full.intval;
62  }
63  } else if (!PSY_PROP(bp->bat, ENERGY_FULL_DESIGN, &bp->full) ||
64  !PSY_PROP(bp->bat, ENERGY_FULL, &bp->full)) {
65  if (bp->full.intval > bp->max_energy) {
66  bp->max_energy_bat = bp->bat;
67  bp->max_energy = bp->full.intval;
68  }
69  }
70  return 0;
71 }
72 
73 static void find_main_battery(void)
74 {
75  struct find_bat_param bp;
76  int error;
77 
78  memset(&bp, 0, sizeof(struct find_bat_param));
79  main_battery = NULL;
80  bp.main = main_battery;
81 
83  __find_main_battery);
84  if (error) {
85  main_battery = bp.main;
86  return;
87  }
88 
89  if ((bp.max_energy_bat && bp.max_charge_bat) &&
90  (bp.max_energy_bat != bp.max_charge_bat)) {
91  /* try guess battery with more capacity */
92  if (!PSY_PROP(bp.max_charge_bat, VOLTAGE_MAX_DESIGN,
93  &bp.full)) {
94  if (bp.max_energy > bp.max_charge * bp.full.intval)
95  main_battery = bp.max_energy_bat;
96  else
97  main_battery = bp.max_charge_bat;
98  } else if (!PSY_PROP(bp.max_energy_bat, VOLTAGE_MAX_DESIGN,
99  &bp.full)) {
100  if (bp.max_charge > bp.max_energy / bp.full.intval)
101  main_battery = bp.max_charge_bat;
102  else
103  main_battery = bp.max_energy_bat;
104  } else {
105  /* give up, choice any */
106  main_battery = bp.max_energy_bat;
107  }
108  } else if (bp.max_charge_bat) {
109  main_battery = bp.max_charge_bat;
110  } else if (bp.max_energy_bat) {
111  main_battery = bp.max_energy_bat;
112  } else {
113  /* give up, try the last if any */
114  main_battery = bp.bat;
115  }
116 }
117 
118 static int do_calculate_time(int status, enum apm_source source)
119 {
123  union power_supply_propval I;
124  enum power_supply_property full_prop;
125  enum power_supply_property full_design_prop;
126  enum power_supply_property empty_prop;
127  enum power_supply_property empty_design_prop;
128  enum power_supply_property cur_avg_prop;
129  enum power_supply_property cur_now_prop;
130 
131  if (MPSY_PROP(CURRENT_AVG, &I)) {
132  /* if battery can't report average value, use momentary */
133  if (MPSY_PROP(CURRENT_NOW, &I))
134  return -1;
135  }
136 
137  if (!I.intval)
138  return 0;
139 
140  switch (source) {
141  case SOURCE_CHARGE:
142  full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
143  full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
144  empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
145  empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
146  cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
147  cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
148  break;
149  case SOURCE_ENERGY:
150  full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
151  full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
152  empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
153  empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
154  cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
155  cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
156  break;
157  case SOURCE_VOLTAGE:
158  full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
159  full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
160  empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
161  empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
162  cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
163  cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
164  break;
165  default:
166  printk(KERN_ERR "Unsupported source: %d\n", source);
167  return -1;
168  }
169 
170  if (_MPSY_PROP(full_prop, &full)) {
171  /* if battery can't report this property, use design value */
172  if (_MPSY_PROP(full_design_prop, &full))
173  return -1;
174  }
175 
176  if (_MPSY_PROP(empty_prop, &empty)) {
177  /* if battery can't report this property, use design value */
178  if (_MPSY_PROP(empty_design_prop, &empty))
179  empty.intval = 0;
180  }
181 
182  if (_MPSY_PROP(cur_avg_prop, &cur)) {
183  /* if battery can't report average value, use momentary */
184  if (_MPSY_PROP(cur_now_prop, &cur))
185  return -1;
186  }
187 
188  if (status == POWER_SUPPLY_STATUS_CHARGING)
189  return ((cur.intval - full.intval) * 60L) / I.intval;
190  else
191  return -((cur.intval - empty.intval) * 60L) / I.intval;
192 }
193 
194 static int calculate_time(int status)
195 {
196  int time;
197 
198  time = do_calculate_time(status, SOURCE_ENERGY);
199  if (time != -1)
200  return time;
201 
202  time = do_calculate_time(status, SOURCE_CHARGE);
203  if (time != -1)
204  return time;
205 
206  time = do_calculate_time(status, SOURCE_VOLTAGE);
207  if (time != -1)
208  return time;
209 
210  return -1;
211 }
212 
213 static int calculate_capacity(enum apm_source source)
214 {
215  enum power_supply_property full_prop, empty_prop;
216  enum power_supply_property full_design_prop, empty_design_prop;
217  enum power_supply_property now_prop, avg_prop;
219  int ret;
220 
221  switch (source) {
222  case SOURCE_CHARGE:
223  full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
224  empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
225  full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
226  empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
227  now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
228  avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
229  break;
230  case SOURCE_ENERGY:
231  full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
232  empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
233  full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
234  empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
235  now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
236  avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
237  break;
238  case SOURCE_VOLTAGE:
239  full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
240  empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
241  full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
242  empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
245  break;
246  default:
247  printk(KERN_ERR "Unsupported source: %d\n", source);
248  return -1;
249  }
250 
251  if (_MPSY_PROP(full_prop, &full)) {
252  /* if battery can't report this property, use design value */
253  if (_MPSY_PROP(full_design_prop, &full))
254  return -1;
255  }
256 
257  if (_MPSY_PROP(avg_prop, &cur)) {
258  /* if battery can't report average value, use momentary */
259  if (_MPSY_PROP(now_prop, &cur))
260  return -1;
261  }
262 
263  if (_MPSY_PROP(empty_prop, &empty)) {
264  /* if battery can't report this property, use design value */
265  if (_MPSY_PROP(empty_design_prop, &empty))
266  empty.intval = 0;
267  }
268 
269  if (full.intval - empty.intval)
270  ret = ((cur.intval - empty.intval) * 100L) /
271  (full.intval - empty.intval);
272  else
273  return -1;
274 
275  if (ret > 100)
276  return 100;
277  else if (ret < 0)
278  return 0;
279 
280  return ret;
281 }
282 
283 static void apm_battery_apm_get_power_status(struct apm_power_info *info)
284 {
285  union power_supply_propval status;
286  union power_supply_propval capacity, time_to_full, time_to_empty;
287 
288  mutex_lock(&apm_mutex);
289  find_main_battery();
290  if (!main_battery) {
291  mutex_unlock(&apm_mutex);
292  return;
293  }
294 
295  /* status */
296 
297  if (MPSY_PROP(STATUS, &status))
298  status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
299 
300  /* ac line status */
301 
302  if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
303  (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
304  (status.intval == POWER_SUPPLY_STATUS_FULL))
306  else
308 
309  /* battery life (i.e. capacity, in percents) */
310 
311  if (MPSY_PROP(CAPACITY, &capacity) == 0) {
312  info->battery_life = capacity.intval;
313  } else {
314  /* try calculate using energy */
315  info->battery_life = calculate_capacity(SOURCE_ENERGY);
316  /* if failed try calculate using charge instead */
317  if (info->battery_life == -1)
318  info->battery_life = calculate_capacity(SOURCE_CHARGE);
319  if (info->battery_life == -1)
320  info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
321  }
322 
323  /* charging status */
324 
325  if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
327  } else {
328  if (info->battery_life > 50)
330  else if (info->battery_life > 5)
332  else
334  }
335  info->battery_flag = info->battery_status;
336 
337  /* time */
338 
339  info->units = APM_UNITS_MINS;
340 
341  if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
342  if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
343  !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
344  info->time = time_to_full.intval / 60;
345  else
346  info->time = calculate_time(status.intval);
347  } else {
348  if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
349  !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
350  info->time = time_to_empty.intval / 60;
351  else
352  info->time = calculate_time(status.intval);
353  }
354 
355  mutex_unlock(&apm_mutex);
356 }
357 
358 static int __init apm_battery_init(void)
359 {
360  printk(KERN_INFO "APM Battery Driver\n");
361 
362  apm_get_power_status = apm_battery_apm_get_power_status;
363  return 0;
364 }
365 
366 static void __exit apm_battery_exit(void)
367 {
369 }
370 
371 module_init(apm_battery_init);
372 module_exit(apm_battery_exit);
373 
374 MODULE_AUTHOR("Eugeny Boger <[email protected]>");
375 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
376 MODULE_LICENSE("GPL");