blob: a108e3beca26fb6c9976910c67903225eefd5118 [file] [log] [blame]
David Ngda1fdc32018-03-22 23:44:56 -07001/*
2 *Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
3 *
4 *Redistribution and use in source and binary forms, with or without
5 *modification, are permitted provided that the following conditions are
6 *met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 *THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 *WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 *ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 *BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 *CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 *SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 *BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 *WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 *OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 *IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <dirent.h>
31#include <errno.h>
32#include <string.h>
33#include <fcntl.h>
34#include <unistd.h>
35#include <cutils/klog.h>
36#include <batteryservice/BatteryService.h>
37#include <cutils/android_reboot.h>
38#include <cutils/properties.h>
Benergy Meenan Ravuri3045c8d2019-11-13 16:38:03 +053039#include <healthd/healthd.h>
David Ngda1fdc32018-03-22 23:44:56 -070040#include "minui/minui.h"
41#include "healthd_msm.h"
42#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
43
44#define HVDCP_CHARGER "USB_HVDCP"
45#define HVDCP_BLINK_TYPE 2
46
47#define RED_LED_PATH "/sys/class/leds/red/brightness"
48#define GREEN_LED_PATH "/sys/class/leds/green/brightness"
49#define BLUE_LED_PATH "/sys/class/leds/blue/brightness"
50#define RED_LED_BLINK_PATH "/sys/class/leds/red/blink"
51#define GREEN_LED_BLINK_PATH "/sys/class/leds/green/blink"
52#define BACKLIGHT_PATH "/sys/class/backlight/panel0-backlight/brightness"
53
54#define CHARGING_ENABLED_PATH "/sys/class/power_supply/battery/charging_enabled"
55#define CHARGER_TYPE_PATH "/sys/class/power_supply/usb/type"
56#define BMS_READY_PATH "/sys/class/power_supply/bms/soc_reporting_ready"
57#define BMS_BATT_INFO_PATH "/sys/class/power_supply/bms/battery_info"
58#define BMS_BATT_INFO_ID_PATH "/sys/class/power_supply/bms/battery_info_id"
59#define BMS_BATT_RES_ID_PATH "/sys/class/power_supply/bms/resistance_id"
60#define PERSIST_BATT_INFO_PATH "/persist/bms/batt_info.txt"
61
62#define CHGR_TAG "charger"
63#define HEALTHD_TAG "healthd_msm"
64#define LOGE(tag, x...) do { KLOG_ERROR(tag, x); } while (0)
65#define LOGW(tag, x...) do { KLOG_WARNING(tag, x); } while (0)
66#define LOGV(tag, x...) do { KLOG_DEBUG(tag, x); } while (0)
67
68enum {
69 RED_LED = 0x01 << 0,
70 GREEN_LED = 0x01 << 1,
71 BLUE_LED = 0x01 << 2,
72};
73
74enum batt_info_params {
75 BATT_INFO_NOTIFY = 0,
76 BATT_INFO_SOC,
77 BATT_INFO_RES_ID,
78 BATT_INFO_VOLTAGE,
79 BATT_INFO_TEMP,
80 BATT_INFO_FCC,
81 BATT_INFO_MAX,
82};
83
84struct led_ctl {
85 int color;
86 const char *path;
87};
88
89struct led_ctl leds[3] =
90 {{RED_LED, RED_LED_PATH},
91 {GREEN_LED, GREEN_LED_PATH},
92 {BLUE_LED, BLUE_LED_PATH}};
93
94#define HVDCP_COLOR_MAP (RED_LED | GREEN_LED)
95
96struct soc_led_color_mapping {
97 int soc;
98 int color;
99};
100
101struct soc_led_color_mapping soc_leds[3] = {
102 {15, RED_LED},
103 {90, RED_LED | GREEN_LED},
104 {100, GREEN_LED},
105};
106
107static int batt_info_cached[BATT_INFO_MAX];
108static bool healthd_msm_err_log_once;
109static int8_t healthd_msm_log_en;
110static int8_t healthd_msm_store_params;
111
112static int write_file_int(char const* path, int value)
113{
114 int fd;
115 char buffer[20];
116 int rc = -1, bytes;
117
118 fd = open(path, O_WRONLY);
119 if (fd >= 0) {
120 bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
121 rc = write(fd, buffer, bytes);
122 close(fd);
123 }
124
125 return rc > 0 ? 0 : -1;
126}
127
128static int set_tricolor_led(int on, int color)
129{
130 int rc, i;
131 char buffer[10];
132
133 for (i = 0; i < (int)ARRAY_SIZE(leds); i++) {
134 if ((color & leds[i].color) && (access(leds[i].path, R_OK | W_OK) == 0)) {
135 rc = write_file_int(leds[i].path, on ? 255 : 0);
136 if (rc < 0)
137 return rc;
138 }
139 }
140
141 return 0;
142}
143
144static bool is_hvdcp_inserted()
145{
146 bool hvdcp = false;
147 char buff[12] = "\0";
148 int fd, cnt;
149
150 fd = open(CHARGER_TYPE_PATH, O_RDONLY);
151 if (fd >= 0) {
152 cnt = read(fd, buff, (sizeof(buff) - 1));
153 if (cnt > 0 && !strncmp(buff, HVDCP_CHARGER, 9))
154 hvdcp = true;
155 close(fd);
156 }
157
158 return hvdcp;
159}
160
161static int get_blink_led_for_hvdcp(void)
162{
163 int ret, rc = 0, bytes;
164 int red_blink_fd = -1, green_blink_fd = -1, type_fd = -1;
165 char buf[20];
166
167 type_fd = open(CHARGER_TYPE_PATH, O_RDONLY);
168
169 if (type_fd < 0) {
170 LOGE(CHGR_TAG, "Could not open USB type node\n");
171 return rc;
172 } else {
173 close(type_fd);
174 ret = write_file_int(GREEN_LED_BLINK_PATH, 0);
175 if (ret < 0) {
176 LOGE(CHGR_TAG, "Fail to write: %s\n", GREEN_LED_BLINK_PATH);
177 } else {
178 rc |= GREEN_LED;
179 }
180
181 ret = write_file_int(RED_LED_BLINK_PATH, 0);
182 if (ret < 0) {
183 LOGE(CHGR_TAG, "Fail to write: %s\n", RED_LED_BLINK_PATH);
184 } else {
185 rc |= RED_LED;
186 }
187 }
188
189 return rc;
190}
191
192#if QTI_BSP
193#define STR_LEN 8
194void healthd_board_mode_charger_draw_battery(
195 struct android::BatteryProperties *batt_prop)
196{
197 char cap_str[STR_LEN];
198 int x, y;
199 int str_len_px;
200 static int char_height = -1, char_width = -1;
201
202 if (char_height == -1 && char_width == -1)
203 gr_font_size(&char_width, &char_height);
204 snprintf(cap_str, (STR_LEN - 1), "%d%%", batt_prop->batteryLevel);
205 str_len_px = gr_measure(cap_str);
206 x = (gr_fb_width() - str_len_px) / 2;
207 y = (gr_fb_height() + char_height) / 2;
208 gr_color(0xa4, 0xc6, 0x39, 255);
209 gr_text(x, y, cap_str, 0);
210}
211#endif
212
213void healthd_board_mode_charger_battery_update(
214 struct android::BatteryProperties *batt_prop)
215{
216 static int blink_for_hvdcp = -1;
217 static int old_color = 0;
218 int i, color, soc, rc;
219 bool blink = false;
220
221 if (blink_for_hvdcp == -1)
222 blink_for_hvdcp = get_blink_led_for_hvdcp();
223
224 if ((blink_for_hvdcp > 0) && is_hvdcp_inserted())
225 blink = true;
226
227 soc = batt_prop->batteryLevel;
228
229 for (i = 0; i < ((int)ARRAY_SIZE(soc_leds) - 1); i++) {
230 if (soc < soc_leds[i].soc)
231 break;
232 }
233 color = soc_leds[i].color;
234
235 if (old_color != color) {
236 if ((color == HVDCP_COLOR_MAP) && blink) {
237 if (blink_for_hvdcp & RED_LED) {
238 rc = write_file_int(RED_LED_BLINK_PATH, HVDCP_BLINK_TYPE);
239 if (rc < 0) {
240 LOGE(CHGR_TAG, "Fail to write: %s\n", RED_LED_BLINK_PATH);
241 return;
242 }
243 }
244 if (blink_for_hvdcp & GREEN_LED) {
245 rc = write_file_int(GREEN_LED_BLINK_PATH, HVDCP_BLINK_TYPE);
246 if (rc < 0) {
247 LOGE(CHGR_TAG, "Fail to write: %s\n", GREEN_LED_BLINK_PATH);
248 return;
249 }
250 }
251 } else {
252 rc = set_tricolor_led(0, old_color);
253 if (rc < 0)
254 LOGE(CHGR_TAG, "Error in setting old_color on tricolor_led\n");
255
256 rc = set_tricolor_led(1, color);
257 if (rc < 0)
258 LOGE(CHGR_TAG, "Error in setting color on tricolor_led\n");
259
260 if (!rc) {
261 old_color = color;
262 LOGV(CHGR_TAG, "soc = %d, set led color 0x%x\n", soc, soc_leds[i].color);
263 }
264 }
265 }
266}
267
268#define BACKLIGHT_ON_LEVEL 100
269#define BACKLIGHT_OFF_LEVEL 0
270void healthd_board_mode_charger_set_backlight(bool en)
271{
272 int rc;
273
274 if (access(BACKLIGHT_PATH, R_OK | W_OK) != 0)
275 {
276 LOGW(CHGR_TAG, "Backlight control not support\n");
277 return;
278 }
279
280 rc = write_file_int(BACKLIGHT_PATH, en ? BACKLIGHT_ON_LEVEL :
281 BACKLIGHT_OFF_LEVEL);
282 if (rc < 0) {
283 LOGE(CHGR_TAG, "Could not write to backlight node : %s\n", strerror(errno));
284 return;
285 }
286
287 LOGV(CHGR_TAG, "set backlight status to %d\n", en);
288}
289
290static inline void get_healthd_props()
291{
292 healthd_msm_log_en = property_get_bool("persist.healthd_msm.log_en", 1);
293 healthd_msm_store_params =
294 property_get_bool("persist.healthd_msm.store_params", 0);
295}
296
297#define WAIT_BMS_READY_TIMES_MAX 200
298#define WAIT_BMS_READY_INTERVAL_USEC 200000
299void healthd_board_mode_charger_init()
300{
301 int ret;
302 char buff[8] = "\0";
303 int charging_enabled = 0;
304 int bms_ready = 0;
305 int wait_count = 0;
306 int fd;
307
308 /* check the charging is enabled or not */
309 fd = open(CHARGING_ENABLED_PATH, O_RDONLY);
310 if (fd < 0)
311 return;
312 ret = read(fd, buff, (sizeof(buff) - 1));
313 close(fd);
314 if (ret > 0) {
315 buff[ret] = '\0';
316 sscanf(buff, "%d\n", &charging_enabled);
317 LOGW(CHGR_TAG, "android charging is %s\n",
318 !!charging_enabled ? "enabled" : "disabled");
319 /* if charging is disabled, reboot and exit power off charging */
320 if (!charging_enabled)
321 android_reboot(ANDROID_RB_RESTART, 0, 0);
322 }
323 fd = open(BMS_READY_PATH, O_RDONLY);
324 if (fd < 0)
325 return;
326 while (1) {
327 ret = read(fd, buff, (sizeof(buff) - 1));
328 if (ret > 0) {
329 buff[ret] = '\0';
330 sscanf(buff, "%d\n", &bms_ready);
331 } else {
332 LOGE(CHGR_TAG, "read soc-ready failed, ret=%d\n", ret);
333 break;
334 }
335
336 if ((bms_ready > 0) || (wait_count++ > WAIT_BMS_READY_TIMES_MAX))
337 break;
338 usleep(WAIT_BMS_READY_INTERVAL_USEC);
339 lseek(fd, 0, SEEK_SET);
340 }
341 close(fd);
342 LOGV(CHGR_TAG, "Checking BMS SoC ready done %d!\n", bms_ready);
343}
344
345static void healthd_batt_info_notify()
346{
347 int rc, fd, id = 0;
348 int bms_ready = 0;
349 int wait_count = 0;
350 char buff[100] = "";
351 int batt_info[BATT_INFO_MAX];
352 char *ptr, *tmp, *temp_str;
353 char path_str[50] = "";
354 bool notify_bms = false;
355
356 if (!healthd_msm_store_params) {
357 return;
358 }
359
360 fd = open(PERSIST_BATT_INFO_PATH, O_RDONLY);
361 if (fd < 0) {
362 LOGW(HEALTHD_TAG, "Error in opening batt_info.txt, fd=%d\n", fd);
363 fd = creat(PERSIST_BATT_INFO_PATH, S_IRWXU);
364 if (fd < 0) {
365 LOGE(HEALTHD_TAG, "Couldn't create file, fd=%d errno=%s\n", fd,
366 strerror(errno));
367 goto out;
368 }
369 LOGV(HEALTHD_TAG, "Created file %s\n", PERSIST_BATT_INFO_PATH);
370 close(fd);
371 goto out;
372 } else {
373 LOGV(HEALTHD_TAG, "opened %s\n", PERSIST_BATT_INFO_PATH);
374 }
375
376 rc = read(fd, buff, (sizeof(buff) - 1));
377 if (rc < 0) {
378 LOGE(HEALTHD_TAG, "Error in reading fd %d, rc=%d\n", fd, rc);
379 close(fd);
380 goto out;
381 }
382 close(fd);
383 buff[rc] = '\0';
384 temp_str = strtok_r(buff, ":", &ptr);
385 id = 1;
386 while (temp_str != NULL && id < BATT_INFO_MAX) {
387 batt_info[id++] = (int)strtol(temp_str, &tmp, 10);
388 temp_str = strtok_r(NULL, ":", &ptr);
389 }
390
391 if (id < BATT_INFO_MAX) {
392 LOGE(HEALTHD_TAG, "Read %d batt_info parameters\n", id);
393 goto out;
394 }
395
396 /* Send batt_info parameters to FG driver */
397 for (id = 1; id < BATT_INFO_MAX; id++) {
398 snprintf(path_str, sizeof(path_str), "%s", BMS_BATT_INFO_ID_PATH);
399 rc = write_file_int(path_str, id);
400 if (rc < 0) {
401 LOGE(HEALTHD_TAG, "Error in writing batt_info_id %d, rc=%d\n", id,
402 rc);
403 goto out;
404 }
405
406 snprintf(path_str, sizeof(path_str), "%s", BMS_BATT_INFO_PATH);
407 rc = write_file_int(path_str, batt_info[id]);
408 if (rc < 0) {
409 LOGE(HEALTHD_TAG, "Error in writing batt_info %d, rc=%d\n",
410 batt_info[id], rc);
411 goto out;
412 }
413 }
414
415 notify_bms = true;
416
417out:
418 fd = open(BMS_READY_PATH, O_RDONLY);
419 if (fd < 0) {
420 LOGE(HEALTHD_TAG, "Couldn't open %s\n", BMS_READY_PATH);
421 return;
422 }
423
424 /* Wait for soc_reporting_ready */
425 wait_count = 0;
426 memset(buff, 0, sizeof(buff));
427 while (1) {
428 rc = read(fd, buff, 1);
429 if (rc > 0) {
430 sscanf(buff, "%d\n", &bms_ready);
431 } else {
432 LOGE(HEALTHD_TAG, "read soc-ready failed, rc=%d\n", rc);
433 break;
434 }
435
436 if ((bms_ready > 0) || (wait_count++ > WAIT_BMS_READY_TIMES_MAX))
437 break;
438
439 usleep(WAIT_BMS_READY_INTERVAL_USEC);
440 lseek(fd, 0, SEEK_SET);
441 }
442 close(fd);
443
444 if (!bms_ready)
445 notify_bms = false;
446
447 if (!notify_bms) {
448 LOGE(HEALTHD_TAG, "Not notifying BMS\n");
449 return;
450 }
451
452 /* Notify FG driver */
453 snprintf(path_str, sizeof(path_str), "%s", BMS_BATT_INFO_ID_PATH);
454 rc = write_file_int(path_str, BATT_INFO_NOTIFY);
455 if (rc < 0) {
456 LOGE(HEALTHD_TAG, "Error in writing batt_info_id, rc=%d\n", rc);
457 return;
458 }
459
460 snprintf(path_str, sizeof(path_str), "%s", BMS_BATT_INFO_PATH);
461 rc = write_file_int(path_str, INT_MAX - 1);
462 if (rc < 0)
463 LOGE(HEALTHD_TAG, "Error in writing batt_info, rc=%d\n", rc);
464}
465
466void healthd_board_init(struct healthd_config*)
467{
468 // use defaults
469 get_healthd_props();
470 power_off_alarm_init();
471 healthd_batt_info_notify();
472}
473
474static void healthd_store_batt_props(const struct android::BatteryProperties* props)
475{
476 char buff[100];
477 int fd, rc, len, batteryId = 0;
478
479 if (!healthd_msm_store_params) {
480 return;
481 }
482
483 if (!props->batteryPresent) {
484 return;
485 }
486
487 if (props->batteryLevel == 0 || props->batteryVoltage == 0) {
488 return;
489 }
490
491 memset(buff, 0, sizeof(buff));
492 fd = open(BMS_BATT_RES_ID_PATH, O_RDONLY);
493 if (fd < 0) {
494 if (!healthd_msm_err_log_once) {
495 LOGE(HEALTHD_TAG, "Couldn't open %s\n", BMS_BATT_RES_ID_PATH);
496 healthd_msm_err_log_once = true;
497 }
498 } else {
499 rc = read(fd, buff, 6);
500 if (rc > 0) {
501 sscanf(buff, "%d\n", &batteryId);
502 batteryId /= 1000;
503 } else if (!healthd_msm_err_log_once) {
504 LOGE(HEALTHD_TAG, "reading batt_res_id failed, rc=%d\n", rc);
505 healthd_msm_err_log_once = true;
506 }
507 }
508
509 if (fd >= 0)
510 close(fd);
511
512 if (props->batteryLevel == batt_info_cached[BATT_INFO_SOC] &&
513 props->batteryVoltage == batt_info_cached[BATT_INFO_VOLTAGE] &&
514 props->batteryTemperature == batt_info_cached[BATT_INFO_TEMP] &&
515 props->batteryFullCharge == batt_info_cached[BATT_INFO_FCC] &&
516 batteryId == batt_info_cached[BATT_INFO_RES_ID])
517 return;
518
519 fd = open(PERSIST_BATT_INFO_PATH, O_RDWR | O_TRUNC);
520 if (fd < 0) {
521 /*
522 * Print the error just only once as this function can be called as
523 * long as the system is running and logs should not flood the console.
524 */
525 if (!healthd_msm_err_log_once) {
526 LOGE(HEALTHD_TAG, "Error in opening batt_info.txt, fd=%d\n", fd);
527 healthd_msm_err_log_once = true;
528 }
529 return;
530 }
531
532 len = snprintf(buff, sizeof(buff), "%d:%d:%d:%d:%d", props->batteryLevel,
533 batteryId, props->batteryVoltage,
534 props->batteryTemperature, props->batteryFullCharge);
535 if (len < 0) {
536 if (!healthd_msm_err_log_once) {
537 LOGE(HEALTHD_TAG, "Error in printing to buff, len=%d\n", len);
538 healthd_msm_err_log_once = true;
539 }
540 goto out;
541 }
542
543 buff[len] = '\0';
544 rc = write(fd, buff, sizeof(buff));
545 if (rc < 0) {
546 if (!healthd_msm_err_log_once) {
547 LOGE(HEALTHD_TAG, "Error in writing to batt_info.txt, rc=%d\n", rc);
548 healthd_msm_err_log_once = true;
549 }
550 goto out;
551 }
552
553 batt_info_cached[BATT_INFO_SOC] = props->batteryLevel;
554 batt_info_cached[BATT_INFO_RES_ID] = batteryId;
555 batt_info_cached[BATT_INFO_VOLTAGE] = props->batteryVoltage;
556 batt_info_cached[BATT_INFO_TEMP] = props->batteryTemperature;
557 batt_info_cached[BATT_INFO_FCC] = props->batteryFullCharge;
558
559out:
560 if (fd >= 0)
561 close(fd);
562}
563
564int healthd_board_battery_update(struct android::BatteryProperties* props)
565{
566 // return 0 to log periodic polled battery status to kernel log
567 healthd_store_batt_props(props);
568 if (healthd_msm_log_en)
569 return 0;
570 return 1;
571}