1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "healthd-manta" 18 #include <errno.h> 19 #include <fcntl.h> 20 #include <healthd.h> 21 #include <time.h> 22 #include <unistd.h> 23 #include <batteryservice/BatteryService.h> 24 #include <cutils/klog.h> 25 #include <sys/stat.h> 26 #include <sys/types.h> 27 28 #define POWER_SUPPLY_SUBSYSTEM "power_supply" 29 #define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM 30 31 #define DS2784_PATH POWER_SUPPLY_SYSFS_PATH "/ds2784-fuelgauge" 32 33 using namespace android; 34 35 #define TEMP_HIGH_THRESHOLD 600 /* 60C */ 36 #define TEMP_HIGH_RECOVERY 420 /* 42C */ 37 #define TEMP_LOW_RECOVERY 0 /* 0C */ 38 #define TEMP_LOW_THRESHOLD -50 /* -5C */ 39 40 #define FULL_CHARGING_TIME (12 * 60 * 60) 41 #define RECHARGING_TIME (2 * 60 * 60) 42 #define RECHARGING_VOLTAGE (4250) 43 44 static bool manta_bat_recharging; 45 static time_t manta_bat_charging_start_time; 46 47 static unsigned int manta_bat_health = BATTERY_HEALTH_GOOD; 48 static unsigned int manta_bat_charging_status = BATTERY_STATUS_DISCHARGING; 49 static bool manta_bat_batterypresent; 50 51 static int charge_enabled_fd; 52 53 static void manta_bat_check_temp(struct BatteryProperties *props) 54 { 55 if (props->chargerAcOnline == false && 56 props->chargerUsbOnline == false) 57 return; 58 59 if (props->batteryTemperature >= TEMP_HIGH_THRESHOLD) { 60 if (manta_bat_health != BATTERY_HEALTH_OVERHEAT && 61 manta_bat_health != BATTERY_HEALTH_UNSPECIFIED_FAILURE) { 62 KLOG_INFO(LOG_TAG, 63 "battery overheat (%d), charging unavailable\n", 64 props->batteryTemperature); 65 manta_bat_health = BATTERY_HEALTH_OVERHEAT; 66 } 67 } else if (props->batteryTemperature <= TEMP_HIGH_RECOVERY && 68 props->batteryTemperature >= TEMP_LOW_RECOVERY) { 69 if (manta_bat_health == BATTERY_HEALTH_OVERHEAT || 70 manta_bat_health == BATTERY_HEALTH_COLD) { 71 KLOG_INFO(LOG_TAG, 72 "battery recovery (%d), charging available\n", 73 props->batteryTemperature); 74 manta_bat_health = BATTERY_HEALTH_GOOD; 75 } 76 } else if (props->batteryTemperature <= TEMP_LOW_THRESHOLD) { 77 if (manta_bat_health != BATTERY_HEALTH_COLD && 78 manta_bat_health != BATTERY_HEALTH_UNSPECIFIED_FAILURE) { 79 KLOG_INFO(LOG_TAG, 80 "battery cold (%d), charging unavailable\n", 81 props->batteryTemperature); 82 manta_bat_health = BATTERY_HEALTH_COLD; 83 } 84 } 85 } 86 87 static void manta_bat_set_charge_time(bool enable) 88 { 89 if (enable && !manta_bat_charging_start_time) 90 time(&manta_bat_charging_start_time); 91 else if (!enable) 92 manta_bat_charging_start_time = 0; 93 } 94 95 static void manta_bat_enable_charging(bool enable) 96 { 97 int ret; 98 char val[20]; 99 100 if (enable && (manta_bat_health != BATTERY_HEALTH_GOOD)) { 101 manta_bat_charging_status = BATTERY_STATUS_NOT_CHARGING; 102 return; 103 } 104 105 if (charge_enabled_fd < 0) 106 goto err; 107 108 snprintf(val, sizeof(val), "%d", enable); 109 ret = write(charge_enabled_fd, val, strlen(val)); 110 if (ret == -1) { 111 KLOG_ERROR(LOG_TAG, "charge_enabled write error; errno=%d\n", errno); 112 goto err; 113 } 114 115 manta_bat_set_charge_time(enable); 116 KLOG_INFO(LOG_TAG, "battery charge enable=%d\n", enable); 117 return; 118 119 err: 120 if (enable) 121 manta_bat_charging_status = BATTERY_STATUS_NOT_CHARGING; 122 } 123 124 static bool manta_bat_charge_timeout(time_t timeout) 125 { 126 if (!manta_bat_charging_start_time) 127 return false; 128 129 return time(NULL) >= manta_bat_charging_start_time + timeout; 130 } 131 132 static void manta_bat_set_full(void) 133 { 134 KLOG_INFO(LOG_TAG, "battery full\n"); 135 manta_bat_charging_status = BATTERY_STATUS_FULL; 136 manta_bat_enable_charging(false); 137 manta_bat_recharging = false; 138 } 139 140 static void manta_bat_charging_timer(struct BatteryProperties *props) 141 { 142 if (!manta_bat_charging_start_time && 143 manta_bat_charging_status == BATTERY_STATUS_CHARGING) { 144 KLOG_WARNING("battery charging timer not started, starting\n"); 145 manta_bat_enable_charging(true); 146 manta_bat_recharging = true; 147 } else if (!manta_bat_charging_start_time) { 148 return; 149 } 150 151 if (manta_bat_charge_timeout(manta_bat_recharging ? 152 RECHARGING_TIME : FULL_CHARGING_TIME)) { 153 KLOG_INFO(LOG_TAG, "battery charging timer expired\n"); 154 if (props->batteryVoltage > RECHARGING_VOLTAGE && 155 props->batteryLevel == 100) { 156 manta_bat_set_full(); 157 } else { 158 manta_bat_enable_charging(false); 159 manta_bat_recharging = false; 160 manta_bat_charging_start_time = 0; 161 } 162 } 163 } 164 165 static void manta_bat_check_charge_source_changed( 166 struct BatteryProperties *props) 167 { 168 if (props->chargerUsbOnline || props->chargerAcOnline) { 169 if (manta_bat_charging_status == BATTERY_STATUS_CHARGING || 170 (manta_bat_charging_status == BATTERY_STATUS_FULL && 171 manta_bat_recharging)) 172 return; 173 174 /* 175 * If charging status indicates a charger was already 176 * connected prior to this and the status is something 177 * other than charging ("full" or "not-charging"), leave 178 * the status alone. 179 */ 180 if (manta_bat_charging_status == BATTERY_STATUS_DISCHARGING || 181 manta_bat_charging_status == BATTERY_STATUS_UNKNOWN) 182 manta_bat_charging_status = BATTERY_STATUS_CHARGING; 183 184 /* 185 * Don't re-enable charging if the battery is full and we 186 * are not actively re-charging it, or if "not-charging" 187 * status is set. 188 */ 189 if (!(manta_bat_charging_status == BATTERY_STATUS_FULL 190 && !manta_bat_recharging) && 191 manta_bat_charging_status != BATTERY_STATUS_NOT_CHARGING) 192 manta_bat_enable_charging(true); 193 } else if (manta_bat_charging_status == BATTERY_STATUS_CHARGING || 194 manta_bat_charging_status == BATTERY_STATUS_NOT_CHARGING || 195 manta_bat_charging_status == BATTERY_STATUS_FULL) { 196 manta_bat_charging_status = BATTERY_STATUS_DISCHARGING; 197 manta_bat_enable_charging(false); 198 manta_bat_health = BATTERY_HEALTH_GOOD; 199 manta_bat_recharging = false; 200 manta_bat_charging_start_time = 0; 201 } 202 } 203 204 static void manta_bat_monitor(struct BatteryProperties *props) 205 { 206 unsigned int old_bat_health = manta_bat_health; 207 208 if (manta_bat_batterypresent) { 209 manta_bat_check_temp(props); 210 } else { 211 props->batteryTemperature = 42; /* 4.2C */ 212 props->batteryVoltage = 4242; /* 4242mV */ 213 props->batteryLevel = 42; /* 42% */ 214 props->batteryCurrentNow = 42000;/* 42mA */ 215 } 216 217 if (props->batteryStatus == BATTERY_STATUS_FULL && 218 (manta_bat_charging_status == BATTERY_STATUS_CHARGING || 219 manta_bat_recharging)) { 220 manta_bat_set_full(); 221 } 222 223 manta_bat_check_charge_source_changed(props); 224 225 switch (manta_bat_charging_status) { 226 case BATTERY_STATUS_FULL: 227 if (props->batteryVoltage < RECHARGING_VOLTAGE && 228 !manta_bat_recharging) { 229 KLOG_INFO(LOG_TAG, "start recharging, v=%d\n", 230 props->batteryVoltage); 231 manta_bat_recharging = true; 232 manta_bat_enable_charging(true); 233 } 234 break; 235 case BATTERY_STATUS_DISCHARGING: 236 break; 237 case BATTERY_STATUS_CHARGING: 238 switch (manta_bat_health) { 239 case BATTERY_HEALTH_OVERHEAT: 240 case BATTERY_HEALTH_COLD: 241 case BATTERY_HEALTH_OVER_VOLTAGE: 242 case BATTERY_HEALTH_DEAD: 243 case BATTERY_HEALTH_UNSPECIFIED_FAILURE: 244 manta_bat_charging_status = BATTERY_STATUS_NOT_CHARGING; 245 manta_bat_enable_charging(false); 246 KLOG_INFO(LOG_TAG, "Not charging, health=%d\n", 247 manta_bat_health); 248 break; 249 default: 250 break; 251 } 252 break; 253 case BATTERY_STATUS_NOT_CHARGING: 254 if (old_bat_health != BATTERY_HEALTH_GOOD && 255 manta_bat_health == BATTERY_HEALTH_GOOD) { 256 KLOG_INFO(LOG_TAG, "battery health recovered\n"); 257 258 if (props->chargerUsbOnline || props->chargerAcOnline) { 259 manta_bat_enable_charging(true); 260 manta_bat_charging_status = BATTERY_STATUS_CHARGING; 261 } else { 262 manta_bat_charging_status = 263 BATTERY_STATUS_DISCHARGING; 264 } 265 } 266 break; 267 default: 268 break; 269 } 270 271 manta_bat_charging_timer(props); 272 273 // set health and status according to our state, hardcode invariants 274 props->batteryHealth = manta_bat_health; 275 props->batteryStatus = manta_bat_charging_status; 276 props->batteryTechnology = "Li-ion"; 277 props->batteryPresent = manta_bat_batterypresent; 278 } 279 280 int healthd_board_battery_update(struct BatteryProperties *props) 281 { 282 manta_bat_monitor(props); 283 284 // return 0 to log periodic polled battery status to kernel log 285 return 0; 286 } 287 288 void healthd_board_init(struct healthd_config *config) 289 { 290 charge_enabled_fd = open(POWER_SUPPLY_SYSFS_PATH 291 "/manta-battery/charge_enabled", O_WRONLY); 292 if (charge_enabled_fd == -1) 293 KLOG_ERROR(LOG_TAG, "open manta-battery/charge_enabled failed" 294 "; errno=%d\n", errno); 295 296 config->batteryCurrentNowPath = DS2784_PATH "/current_now"; 297 298 if (access(config->batteryCurrentNowPath.string(), R_OK) == 0) { 299 manta_bat_batterypresent = true; 300 } else { 301 KLOG_INFO("Missing battery, using fake battery data\n"); 302 config->batteryCurrentNowPath.clear(); 303 } 304 } 305