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
manta_bat_check_temp(struct BatteryProperties * props)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
manta_bat_set_charge_time(bool enable)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
manta_bat_enable_charging(bool enable)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
manta_bat_charge_timeout(time_t timeout)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
manta_bat_set_full(void)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
manta_bat_charging_timer(struct BatteryProperties * props)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
manta_bat_check_charge_source_changed(struct BatteryProperties * props)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
manta_bat_monitor(struct BatteryProperties * props)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
healthd_board_battery_update(struct BatteryProperties * props)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
healthd_board_init(struct healthd_config * config)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