Blame view

drivers/power/apm_power.c 10.1 KB
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
   * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
   *
   * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
   *
   * Use consistent with the GNU GPL is permitted,
   * provided that this copyright notice is
   * preserved in its entirety in all copies and derived works.
   */
  
  #include <linux/module.h>
  #include <linux/power_supply.h>
  #include <linux/apm-emulation.h>
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
15

c84cad3d0   Ken O'Brien   apm_power: Fix st...
16
17
  #define PSY_PROP(psy, prop, val) (psy->get_property(psy, \
  			 POWER_SUPPLY_PROP_##prop, val))
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
18

c84cad3d0   Ken O'Brien   apm_power: Fix st...
19
20
  #define _MPSY_PROP(prop, val) (main_battery->get_property(main_battery, \
  							 prop, val))
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
21
22
  
  #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
23
  static DEFINE_MUTEX(apm_mutex);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
24
  static struct power_supply *main_battery;
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
25
26
27
28
29
  enum apm_source {
  	SOURCE_ENERGY,
  	SOURCE_CHARGE,
  	SOURCE_VOLTAGE,
  };
443cad920   Dave Young   power supply : us...
30
31
32
33
34
  struct find_bat_param {
  	struct power_supply *main;
  	struct power_supply *bat;
  	struct power_supply *max_charge_bat;
  	struct power_supply *max_energy_bat;
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
35
  	union power_supply_propval full;
443cad920   Dave Young   power supply : us...
36
37
38
  	int max_charge;
  	int max_energy;
  };
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
39

443cad920   Dave Young   power supply : us...
40
41
42
  static int __find_main_battery(struct device *dev, void *data)
  {
  	struct find_bat_param *bp = (struct find_bat_param *)data;
d385376f0   Anton Vorontsov   apm_power: improv...
43

443cad920   Dave Young   power supply : us...
44
  	bp->bat = dev_get_drvdata(dev);
d385376f0   Anton Vorontsov   apm_power: improv...
45

443cad920   Dave Young   power supply : us...
46
47
48
49
50
  	if (bp->bat->use_for_apm) {
  		/* nice, we explicitly asked to report this battery. */
  		bp->main = bp->bat;
  		return 1;
  	}
d385376f0   Anton Vorontsov   apm_power: improv...
51

443cad920   Dave Young   power supply : us...
52
53
54
55
56
57
58
59
60
61
62
  	if (!PSY_PROP(bp->bat, CHARGE_FULL_DESIGN, &bp->full) ||
  			!PSY_PROP(bp->bat, CHARGE_FULL, &bp->full)) {
  		if (bp->full.intval > bp->max_charge) {
  			bp->max_charge_bat = bp->bat;
  			bp->max_charge = bp->full.intval;
  		}
  	} else if (!PSY_PROP(bp->bat, ENERGY_FULL_DESIGN, &bp->full) ||
  			!PSY_PROP(bp->bat, ENERGY_FULL, &bp->full)) {
  		if (bp->full.intval > bp->max_energy) {
  			bp->max_energy_bat = bp->bat;
  			bp->max_energy = bp->full.intval;
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
63
  		}
d385376f0   Anton Vorontsov   apm_power: improv...
64
  	}
443cad920   Dave Young   power supply : us...
65
66
67
68
69
70
71
72
73
74
75
  	return 0;
  }
  
  static void find_main_battery(void)
  {
  	struct find_bat_param bp;
  	int error;
  
  	memset(&bp, 0, sizeof(struct find_bat_param));
  	main_battery = NULL;
  	bp.main = main_battery;
93562b537   Greg Kroah-Hartman   Driver Core: add ...
76
  	error = class_for_each_device(power_supply_class, NULL, &bp,
443cad920   Dave Young   power supply : us...
77
78
79
80
81
  				      __find_main_battery);
  	if (error) {
  		main_battery = bp.main;
  		return;
  	}
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
82

443cad920   Dave Young   power supply : us...
83
84
  	if ((bp.max_energy_bat && bp.max_charge_bat) &&
  			(bp.max_energy_bat != bp.max_charge_bat)) {
d385376f0   Anton Vorontsov   apm_power: improv...
85
  		/* try guess battery with more capacity */
443cad920   Dave Young   power supply : us...
86
87
88
89
  		if (!PSY_PROP(bp.max_charge_bat, VOLTAGE_MAX_DESIGN,
  			      &bp.full)) {
  			if (bp.max_energy > bp.max_charge * bp.full.intval)
  				main_battery = bp.max_energy_bat;
d385376f0   Anton Vorontsov   apm_power: improv...
90
  			else
443cad920   Dave Young   power supply : us...
91
92
93
94
95
  				main_battery = bp.max_charge_bat;
  		} else if (!PSY_PROP(bp.max_energy_bat, VOLTAGE_MAX_DESIGN,
  								  &bp.full)) {
  			if (bp.max_charge > bp.max_energy / bp.full.intval)
  				main_battery = bp.max_charge_bat;
d385376f0   Anton Vorontsov   apm_power: improv...
96
  			else
443cad920   Dave Young   power supply : us...
97
  				main_battery = bp.max_energy_bat;
d385376f0   Anton Vorontsov   apm_power: improv...
98
99
  		} else {
  			/* give up, choice any */
443cad920   Dave Young   power supply : us...
100
  			main_battery = bp.max_energy_bat;
d385376f0   Anton Vorontsov   apm_power: improv...
101
  		}
443cad920   Dave Young   power supply : us...
102
103
104
105
  	} else if (bp.max_charge_bat) {
  		main_battery = bp.max_charge_bat;
  	} else if (bp.max_energy_bat) {
  		main_battery = bp.max_energy_bat;
d385376f0   Anton Vorontsov   apm_power: improv...
106
107
  	} else {
  		/* give up, try the last if any */
443cad920   Dave Young   power supply : us...
108
  		main_battery = bp.bat;
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
109
  	}
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
110
  }
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
111
  static int do_calculate_time(int status, enum apm_source source)
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
112
  {
2a721dfc8   Anton Vorontsov   apm_power: calcul...
113
114
115
116
117
118
119
120
121
122
  	union power_supply_propval full;
  	union power_supply_propval empty;
  	union power_supply_propval cur;
  	union power_supply_propval I;
  	enum power_supply_property full_prop;
  	enum power_supply_property full_design_prop;
  	enum power_supply_property empty_prop;
  	enum power_supply_property empty_design_prop;
  	enum power_supply_property cur_avg_prop;
  	enum power_supply_property cur_now_prop;
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
123

2a721dfc8   Anton Vorontsov   apm_power: calcul...
124
125
126
  	if (MPSY_PROP(CURRENT_AVG, &I)) {
  		/* if battery can't report average value, use momentary */
  		if (MPSY_PROP(CURRENT_NOW, &I))
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
127
128
  			return -1;
  	}
e91926e9e   Anton Vorontsov   apm_power: check ...
129
130
  	if (!I.intval)
  		return 0;
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
131
132
  	switch (source) {
  	case SOURCE_CHARGE:
2a721dfc8   Anton Vorontsov   apm_power: calcul...
133
134
135
136
137
138
  		full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
  		full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
  		empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
  		empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
  		cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
  		cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
139
140
  		break;
  	case SOURCE_ENERGY:
2a721dfc8   Anton Vorontsov   apm_power: calcul...
141
142
143
144
145
146
  		full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
  		full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
  		empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
  		empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
  		cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
  		cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
147
148
149
150
151
152
153
154
155
156
157
158
159
  		break;
  	case SOURCE_VOLTAGE:
  		full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
  		full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
  		empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
  		empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
  		cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
  		cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
  		break;
  	default:
  		printk(KERN_ERR "Unsupported source: %d
  ", source);
  		return -1;
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
160
  	}
2a721dfc8   Anton Vorontsov   apm_power: calcul...
161
162
163
  	if (_MPSY_PROP(full_prop, &full)) {
  		/* if battery can't report this property, use design value */
  		if (_MPSY_PROP(full_design_prop, &full))
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
164
165
  			return -1;
  	}
2a721dfc8   Anton Vorontsov   apm_power: calcul...
166
167
168
169
170
171
172
  	if (_MPSY_PROP(empty_prop, &empty)) {
  		/* if battery can't report this property, use design value */
  		if (_MPSY_PROP(empty_design_prop, &empty))
  			empty.intval = 0;
  	}
  
  	if (_MPSY_PROP(cur_avg_prop, &cur)) {
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
173
  		/* if battery can't report average value, use momentary */
2a721dfc8   Anton Vorontsov   apm_power: calcul...
174
  		if (_MPSY_PROP(cur_now_prop, &cur))
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
175
176
177
178
  			return -1;
  	}
  
  	if (status == POWER_SUPPLY_STATUS_CHARGING)
2a721dfc8   Anton Vorontsov   apm_power: calcul...
179
  		return ((cur.intval - full.intval) * 60L) / I.intval;
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
180
  	else
2a721dfc8   Anton Vorontsov   apm_power: calcul...
181
  		return -((cur.intval - empty.intval) * 60L) / I.intval;
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
182
  }
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
  static int calculate_time(int status)
  {
  	int time;
  
  	time = do_calculate_time(status, SOURCE_ENERGY);
  	if (time != -1)
  		return time;
  
  	time = do_calculate_time(status, SOURCE_CHARGE);
  	if (time != -1)
  		return time;
  
  	time = do_calculate_time(status, SOURCE_VOLTAGE);
  	if (time != -1)
  		return time;
  
  	return -1;
  }
  
  static int calculate_capacity(enum apm_source source)
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
203
204
205
206
207
208
  {
  	enum power_supply_property full_prop, empty_prop;
  	enum power_supply_property full_design_prop, empty_design_prop;
  	enum power_supply_property now_prop, avg_prop;
  	union power_supply_propval empty, full, cur;
  	int ret;
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
209
210
  	switch (source) {
  	case SOURCE_CHARGE:
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
211
212
213
214
215
216
  		full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
  		empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
  		full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
  		empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
  		now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
  		avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
217
218
  		break;
  	case SOURCE_ENERGY:
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
219
220
221
222
223
224
  		full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
  		empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
  		full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
  		empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
  		now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
  		avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
1d220334d   Anton Vorontsov   apm_power: Add mi...
225
  		break;
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
226
227
228
229
230
231
232
233
234
235
236
237
  	case SOURCE_VOLTAGE:
  		full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
  		empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
  		full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
  		empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
  		now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
  		avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
  		break;
  	default:
  		printk(KERN_ERR "Unsupported source: %d
  ", source);
  		return -1;
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
  	}
  
  	if (_MPSY_PROP(full_prop, &full)) {
  		/* if battery can't report this property, use design value */
  		if (_MPSY_PROP(full_design_prop, &full))
  			return -1;
  	}
  
  	if (_MPSY_PROP(avg_prop, &cur)) {
  		/* if battery can't report average value, use momentary */
  		if (_MPSY_PROP(now_prop, &cur))
  			return -1;
  	}
  
  	if (_MPSY_PROP(empty_prop, &empty)) {
  		/* if battery can't report this property, use design value */
  		if (_MPSY_PROP(empty_design_prop, &empty))
  			empty.intval = 0;
  	}
  
  	if (full.intval - empty.intval)
  		ret =  ((cur.intval - empty.intval) * 100L) /
  		       (full.intval - empty.intval);
  	else
  		return -1;
  
  	if (ret > 100)
  		return 100;
  	else if (ret < 0)
  		return 0;
  
  	return ret;
  }
  
  static void apm_battery_apm_get_power_status(struct apm_power_info *info)
  {
  	union power_supply_propval status;
  	union power_supply_propval capacity, time_to_full, time_to_empty;
443cad920   Dave Young   power supply : us...
276
  	mutex_lock(&apm_mutex);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
277
278
  	find_main_battery();
  	if (!main_battery) {
443cad920   Dave Young   power supply : us...
279
  		mutex_unlock(&apm_mutex);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
  		return;
  	}
  
  	/* status */
  
  	if (MPSY_PROP(STATUS, &status))
  		status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
  
  	/* ac line status */
  
  	if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
  	    (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
  	    (status.intval == POWER_SUPPLY_STATUS_FULL))
  		info->ac_line_status = APM_AC_ONLINE;
  	else
  		info->ac_line_status = APM_AC_OFFLINE;
  
  	/* battery life (i.e. capacity, in percents) */
  
  	if (MPSY_PROP(CAPACITY, &capacity) == 0) {
  		info->battery_life = capacity.intval;
  	} else {
  		/* try calculate using energy */
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
303
  		info->battery_life = calculate_capacity(SOURCE_ENERGY);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
304
305
  		/* if failed try calculate using charge instead */
  		if (info->battery_life == -1)
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
306
307
308
  			info->battery_life = calculate_capacity(SOURCE_CHARGE);
  		if (info->battery_life == -1)
  			info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  	}
  
  	/* charging status */
  
  	if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
  		info->battery_status = APM_BATTERY_STATUS_CHARGING;
  	} else {
  		if (info->battery_life > 50)
  			info->battery_status = APM_BATTERY_STATUS_HIGH;
  		else if (info->battery_life > 5)
  			info->battery_status = APM_BATTERY_STATUS_LOW;
  		else
  			info->battery_status = APM_BATTERY_STATUS_CRITICAL;
  	}
  	info->battery_flag = info->battery_status;
  
  	/* time */
  
  	info->units = APM_UNITS_MINS;
  
  	if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
cd1ebcc0e   Anton Vorontsov   apm_power: fix ob...
330
  		if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
331
  				!MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
cd1ebcc0e   Anton Vorontsov   apm_power: fix ob...
332
  			info->time = time_to_full.intval / 60;
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
333
334
  		else
  			info->time = calculate_time(status.intval);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
335
  	} else {
cd1ebcc0e   Anton Vorontsov   apm_power: fix ob...
336
  		if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
337
  			      !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
cd1ebcc0e   Anton Vorontsov   apm_power: fix ob...
338
  			info->time = time_to_empty.intval / 60;
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
339
340
  		else
  			info->time = calculate_time(status.intval);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
341
  	}
443cad920   Dave Young   power supply : us...
342
  	mutex_unlock(&apm_mutex);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  }
  
  static int __init apm_battery_init(void)
  {
  	printk(KERN_INFO "APM Battery Driver
  ");
  
  	apm_get_power_status = apm_battery_apm_get_power_status;
  	return 0;
  }
  
  static void __exit apm_battery_exit(void)
  {
  	apm_get_power_status = NULL;
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
357
358
359
360
361
362
363
364
  }
  
  module_init(apm_battery_init);
  module_exit(apm_battery_exit);
  
  MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
  MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
  MODULE_LICENSE("GPL");