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 <stdlib.h>
22 #include <time.h>
23 #include <unistd.h>
24 #include <batteryservice/BatteryService.h>
25 #include <cutils/klog.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28
29 /* Nominal voltage for ENERGY_COUNTER computation */
30 #define VOLTAGE_NOMINAL 3.7
31
32 #define POWER_SUPPLY_SUBSYSTEM "power_supply"
33 #define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
34
35 #define DS2784_PATH POWER_SUPPLY_SYSFS_PATH "/ds2784-fuelgauge"
36 #define CHARGE_COUNTER_EXT_PATH DS2784_PATH "/charge_counter_ext"
37
38 using namespace android;
39
40 #define TEMP_HIGH_THRESHOLD 600 /* 60C */
41 #define TEMP_HIGH_RECOVERY 420 /* 42C */
42 #define TEMP_LOW_RECOVERY 0 /* 0C */
43 #define TEMP_LOW_THRESHOLD -50 /* -5C */
44
45 #define FULL_CHARGING_TIME (12 * 60 * 60)
46 #define RECHARGING_TIME (2 * 60 * 60)
47 #define RECHARGING_VOLTAGE (4250)
48
49 static bool manta_bat_recharging;
50 static time_t manta_bat_charging_start_time;
51
52 static unsigned int manta_bat_health = BATTERY_HEALTH_GOOD;
53 static unsigned int manta_bat_charging_status = BATTERY_STATUS_DISCHARGING;
54 static bool manta_bat_batterypresent;
55
56 static int charge_enabled_fd;
57
manta_bat_check_temp(struct BatteryProperties * props)58 static void manta_bat_check_temp(struct BatteryProperties *props)
59 {
60 if (props->chargerAcOnline == false &&
61 props->chargerUsbOnline == false)
62 return;
63
64 if (props->batteryTemperature >= TEMP_HIGH_THRESHOLD) {
65 if (manta_bat_health != BATTERY_HEALTH_OVERHEAT &&
66 manta_bat_health != BATTERY_HEALTH_UNSPECIFIED_FAILURE) {
67 KLOG_INFO(LOG_TAG,
68 "battery overheat (%d), charging unavailable\n",
69 props->batteryTemperature);
70 manta_bat_health = BATTERY_HEALTH_OVERHEAT;
71 }
72 } else if (props->batteryTemperature <= TEMP_HIGH_RECOVERY &&
73 props->batteryTemperature >= TEMP_LOW_RECOVERY) {
74 if (manta_bat_health == BATTERY_HEALTH_OVERHEAT ||
75 manta_bat_health == BATTERY_HEALTH_COLD) {
76 KLOG_INFO(LOG_TAG,
77 "battery recovery (%d), charging available\n",
78 props->batteryTemperature);
79 manta_bat_health = BATTERY_HEALTH_GOOD;
80 }
81 } else if (props->batteryTemperature <= TEMP_LOW_THRESHOLD) {
82 if (manta_bat_health != BATTERY_HEALTH_COLD &&
83 manta_bat_health != BATTERY_HEALTH_UNSPECIFIED_FAILURE) {
84 KLOG_INFO(LOG_TAG,
85 "battery cold (%d), charging unavailable\n",
86 props->batteryTemperature);
87 manta_bat_health = BATTERY_HEALTH_COLD;
88 }
89 }
90 }
91
manta_bat_set_charge_time(bool enable)92 static void manta_bat_set_charge_time(bool enable)
93 {
94 if (enable && !manta_bat_charging_start_time)
95 time(&manta_bat_charging_start_time);
96 else if (!enable)
97 manta_bat_charging_start_time = 0;
98 }
99
manta_bat_enable_charging(bool enable)100 static void manta_bat_enable_charging(bool enable)
101 {
102 int ret;
103 char val[20];
104
105 if (enable && (manta_bat_health != BATTERY_HEALTH_GOOD)) {
106 manta_bat_charging_status = BATTERY_STATUS_NOT_CHARGING;
107 return;
108 }
109
110 if (charge_enabled_fd < 0)
111 goto err;
112
113 snprintf(val, sizeof(val), "%d", enable);
114 ret = write(charge_enabled_fd, val, strlen(val));
115 if (ret == -1) {
116 KLOG_ERROR(LOG_TAG, "charge_enabled write error; errno=%d\n", errno);
117 goto err;
118 }
119
120 manta_bat_set_charge_time(enable);
121 KLOG_INFO(LOG_TAG, "battery charge enable=%d\n", enable);
122 return;
123
124 err:
125 if (enable)
126 manta_bat_charging_status = BATTERY_STATUS_NOT_CHARGING;
127 }
128
manta_bat_charge_timeout(time_t timeout)129 static bool manta_bat_charge_timeout(time_t timeout)
130 {
131 if (!manta_bat_charging_start_time)
132 return false;
133
134 return time(NULL) >= manta_bat_charging_start_time + timeout;
135 }
136
manta_bat_set_full(void)137 static void manta_bat_set_full(void)
138 {
139 KLOG_INFO(LOG_TAG, "battery full\n");
140 manta_bat_charging_status = BATTERY_STATUS_FULL;
141 manta_bat_enable_charging(false);
142 manta_bat_recharging = false;
143 }
144
manta_bat_charging_timer(struct BatteryProperties * props)145 static void manta_bat_charging_timer(struct BatteryProperties *props)
146 {
147 if (!manta_bat_charging_start_time &&
148 manta_bat_charging_status == BATTERY_STATUS_CHARGING) {
149 KLOG_WARNING("battery charging timer not started, starting\n");
150 manta_bat_enable_charging(true);
151 manta_bat_recharging = true;
152 } else if (!manta_bat_charging_start_time) {
153 return;
154 }
155
156 if (manta_bat_charge_timeout(manta_bat_recharging ?
157 RECHARGING_TIME : FULL_CHARGING_TIME)) {
158 KLOG_INFO(LOG_TAG, "battery charging timer expired\n");
159 if (props->batteryVoltage > RECHARGING_VOLTAGE &&
160 props->batteryLevel == 100) {
161 manta_bat_set_full();
162 } else {
163 manta_bat_enable_charging(false);
164 manta_bat_recharging = false;
165 manta_bat_charging_start_time = 0;
166 }
167 }
168 }
169
manta_bat_check_charge_source_changed(struct BatteryProperties * props)170 static void manta_bat_check_charge_source_changed(
171 struct BatteryProperties *props)
172 {
173 if (props->chargerUsbOnline || props->chargerAcOnline) {
174 if (manta_bat_charging_status == BATTERY_STATUS_CHARGING ||
175 (manta_bat_charging_status == BATTERY_STATUS_FULL &&
176 manta_bat_recharging))
177 return;
178
179 /*
180 * If charging status indicates a charger was already
181 * connected prior to this and the status is something
182 * other than charging ("full" or "not-charging"), leave
183 * the status alone.
184 */
185 if (manta_bat_charging_status == BATTERY_STATUS_DISCHARGING ||
186 manta_bat_charging_status == BATTERY_STATUS_UNKNOWN)
187 manta_bat_charging_status = BATTERY_STATUS_CHARGING;
188
189 /*
190 * Don't re-enable charging if the battery is full and we
191 * are not actively re-charging it, or if "not-charging"
192 * status is set.
193 */
194 if (!(manta_bat_charging_status == BATTERY_STATUS_FULL
195 && !manta_bat_recharging) &&
196 manta_bat_charging_status != BATTERY_STATUS_NOT_CHARGING)
197 manta_bat_enable_charging(true);
198 } else if (manta_bat_charging_status == BATTERY_STATUS_CHARGING ||
199 manta_bat_charging_status == BATTERY_STATUS_NOT_CHARGING ||
200 manta_bat_charging_status == BATTERY_STATUS_FULL) {
201 manta_bat_charging_status = BATTERY_STATUS_DISCHARGING;
202 manta_bat_enable_charging(false);
203 manta_bat_health = BATTERY_HEALTH_GOOD;
204 manta_bat_recharging = false;
205 manta_bat_charging_start_time = 0;
206 }
207 }
208
manta_bat_monitor(struct BatteryProperties * props)209 static void manta_bat_monitor(struct BatteryProperties *props)
210 {
211 unsigned int old_bat_health = manta_bat_health;
212
213 if (manta_bat_batterypresent) {
214 manta_bat_check_temp(props);
215 } else {
216 props->batteryTemperature = 42; /* 4.2C */
217 props->batteryVoltage = 4342; /* 4342mV */
218 props->batteryLevel = 42; /* 42% */
219 }
220
221 if (props->batteryStatus == BATTERY_STATUS_FULL &&
222 (manta_bat_charging_status == BATTERY_STATUS_CHARGING ||
223 manta_bat_recharging)) {
224 manta_bat_set_full();
225 }
226
227 manta_bat_check_charge_source_changed(props);
228
229 switch (manta_bat_charging_status) {
230 case BATTERY_STATUS_FULL:
231 if (props->batteryVoltage < RECHARGING_VOLTAGE &&
232 !manta_bat_recharging) {
233 KLOG_INFO(LOG_TAG, "start recharging, v=%d\n",
234 props->batteryVoltage);
235 manta_bat_recharging = true;
236 manta_bat_enable_charging(true);
237 }
238 break;
239 case BATTERY_STATUS_DISCHARGING:
240 break;
241 case BATTERY_STATUS_CHARGING:
242 switch (manta_bat_health) {
243 case BATTERY_HEALTH_OVERHEAT:
244 case BATTERY_HEALTH_COLD:
245 case BATTERY_HEALTH_OVER_VOLTAGE:
246 case BATTERY_HEALTH_DEAD:
247 case BATTERY_HEALTH_UNSPECIFIED_FAILURE:
248 manta_bat_charging_status = BATTERY_STATUS_NOT_CHARGING;
249 manta_bat_enable_charging(false);
250 KLOG_INFO(LOG_TAG, "Not charging, health=%d\n",
251 manta_bat_health);
252 break;
253 default:
254 break;
255 }
256 break;
257 case BATTERY_STATUS_NOT_CHARGING:
258 if (old_bat_health != BATTERY_HEALTH_GOOD &&
259 manta_bat_health == BATTERY_HEALTH_GOOD) {
260 KLOG_INFO(LOG_TAG, "battery health recovered\n");
261
262 if (props->chargerUsbOnline || props->chargerAcOnline) {
263 manta_bat_enable_charging(true);
264 manta_bat_charging_status = BATTERY_STATUS_CHARGING;
265 } else {
266 manta_bat_charging_status =
267 BATTERY_STATUS_DISCHARGING;
268 }
269 }
270 break;
271 default:
272 break;
273 }
274
275 manta_bat_charging_timer(props);
276
277 // set health and status according to our state, hardcode invariants
278 props->batteryHealth = manta_bat_health;
279 props->batteryStatus = manta_bat_charging_status;
280 props->batteryTechnology = "Li-ion";
281 props->batteryPresent = manta_bat_batterypresent;
282 }
283
healthd_board_battery_update(struct BatteryProperties * props)284 int healthd_board_battery_update(struct BatteryProperties *props)
285 {
286 manta_bat_monitor(props);
287
288 // return 0 to log periodic polled battery status to kernel log
289 return 0;
290 }
291
read_sysfs(const char * path,char * buf,size_t size)292 static int read_sysfs(const char *path, char *buf, size_t size) {
293 char *cp = NULL;
294
295 int fd = open(path, O_RDONLY, 0);
296 if (fd == -1) {
297 KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path);
298 return -1;
299 }
300
301 ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size));
302 if (count > 0)
303 cp = (char *)memrchr(buf, '\n', count);
304
305 if (cp)
306 *cp = '\0';
307 else
308 buf[0] = '\0';
309
310 close(fd);
311 return count;
312 }
313
get_int64_field(const char * path)314 static int64_t get_int64_field(const char *path) {
315 const int SIZE = 21;
316 char buf[SIZE];
317
318 int64_t value = 0;
319 if (read_sysfs(path, buf, SIZE) > 0) {
320 value = strtoll(buf, NULL, 0);
321 }
322 return value;
323 }
324
manta_energy_counter(int64_t * energy)325 static int manta_energy_counter(int64_t *energy)
326 {
327 *energy = get_int64_field(CHARGE_COUNTER_EXT_PATH) * VOLTAGE_NOMINAL;
328 return 0;
329 }
330
healthd_board_init(struct healthd_config * config)331 void healthd_board_init(struct healthd_config *config)
332 {
333 charge_enabled_fd = open(POWER_SUPPLY_SYSFS_PATH
334 "/manta-battery/charge_enabled", O_WRONLY);
335 if (charge_enabled_fd == -1)
336 KLOG_ERROR(LOG_TAG, "open manta-battery/charge_enabled failed"
337 "; errno=%d\n", errno);
338
339 config->batteryCurrentNowPath = DS2784_PATH "/current_now";
340
341 if (access(config->batteryCurrentNowPath.string(), R_OK) == 0) {
342 manta_bat_batterypresent = true;
343 } else {
344 KLOG_INFO(LOG_TAG, "Missing battery, using fake battery data\n");
345 config->batteryCurrentNowPath.clear();
346 }
347
348 config->energyCounter = manta_energy_counter;
349 }
350