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