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
  /*
   * 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>
51990e825   Paul Gortmaker   device.h: cleanup...
13
  #include <linux/device.h>
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
14
15
  #include <linux/power_supply.h>
  #include <linux/apm-emulation.h>
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
16

75599d365   Krzysztof Kozlowski   power_supply: apm...
17
  #define PSY_PROP(psy, prop, val) (power_supply_get_property(psy, \
c84cad3d0   Ken O'Brien   apm_power: Fix st...
18
  			 POWER_SUPPLY_PROP_##prop, val))
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
19

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

443cad920   Dave Young   power supply : us...
41
42
43
  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...
44

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

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

443cad920   Dave Young   power supply : us...
53
54
55
56
57
58
59
60
61
62
63
  	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...
64
  		}
d385376f0   Anton Vorontsov   apm_power: improv...
65
  	}
443cad920   Dave Young   power supply : us...
66
67
68
69
70
71
72
73
74
75
76
  	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 ...
77
  	error = class_for_each_device(power_supply_class, NULL, &bp,
443cad920   Dave Young   power supply : us...
78
79
80
81
82
  				      __find_main_battery);
  	if (error) {
  		main_battery = bp.main;
  		return;
  	}
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
83

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

2a721dfc8   Anton Vorontsov   apm_power: calcul...
125
126
127
  	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...
128
129
  			return -1;
  	}
e91926e9e   Anton Vorontsov   apm_power: check ...
130
131
  	if (!I.intval)
  		return 0;
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
132
133
  	switch (source) {
  	case SOURCE_CHARGE:
2a721dfc8   Anton Vorontsov   apm_power: calcul...
134
135
136
137
138
139
  		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...
140
141
  		break;
  	case SOURCE_ENERGY:
2a721dfc8   Anton Vorontsov   apm_power: calcul...
142
143
144
145
146
147
  		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...
148
149
150
151
152
153
154
155
156
157
158
159
160
  		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...
161
  	}
2a721dfc8   Anton Vorontsov   apm_power: calcul...
162
163
164
  	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...
165
166
  			return -1;
  	}
2a721dfc8   Anton Vorontsov   apm_power: calcul...
167
168
169
170
171
172
173
  	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...
174
  		/* if battery can't report average value, use momentary */
2a721dfc8   Anton Vorontsov   apm_power: calcul...
175
  		if (_MPSY_PROP(cur_now_prop, &cur))
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
176
177
178
179
  			return -1;
  	}
  
  	if (status == POWER_SUPPLY_STATUS_CHARGING)
2a721dfc8   Anton Vorontsov   apm_power: calcul...
180
  		return ((cur.intval - full.intval) * 60L) / I.intval;
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
181
  	else
2a721dfc8   Anton Vorontsov   apm_power: calcul...
182
  		return -((cur.intval - empty.intval) * 60L) / I.intval;
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
183
  }
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
  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...
204
205
206
207
208
209
  {
  	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...
210
211
  	switch (source) {
  	case SOURCE_CHARGE:
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
212
213
214
215
216
217
  		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...
218
219
  		break;
  	case SOURCE_ENERGY:
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
220
221
222
223
224
225
  		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...
226
  		break;
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
227
228
229
230
231
232
233
234
235
236
237
238
  	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...
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
276
  	}
  
  	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...
277
  	mutex_lock(&apm_mutex);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
278
279
  	find_main_battery();
  	if (!main_battery) {
443cad920   Dave Young   power supply : us...
280
  		mutex_unlock(&apm_mutex);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
  		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...
304
  		info->battery_life = calculate_capacity(SOURCE_ENERGY);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
305
306
  		/* if failed try calculate using charge instead */
  		if (info->battery_life == -1)
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
307
308
309
  			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...
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
  	}
  
  	/* 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...
331
  		if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
332
  				!MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
cd1ebcc0e   Anton Vorontsov   apm_power: fix ob...
333
  			info->time = time_to_full.intval / 60;
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
334
335
  		else
  			info->time = calculate_time(status.intval);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
336
  	} else {
cd1ebcc0e   Anton Vorontsov   apm_power: fix ob...
337
  		if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
338
  			      !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
cd1ebcc0e   Anton Vorontsov   apm_power: fix ob...
339
  			info->time = time_to_empty.intval / 60;
dffd28a13   Dmitry Eremin-Solenikov   apm_power: suppor...
340
341
  		else
  			info->time = calculate_time(status.intval);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
342
  	}
443cad920   Dave Young   power supply : us...
343
  	mutex_unlock(&apm_mutex);
3788ec932   Anton Vorontsov   [BATTERY] APM emu...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
  }
  
  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...
358
359
360
361
362
363
364
365
  }
  
  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");