• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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