blob: 29168a15e6b3a5da0c7311977928452d53e2ed0e [file] [log] [blame]
Steve Kondik75956202016-08-07 23:21:29 -07001/*
2 * Copyright (C) 2016 The CyanogenMod Project
Zhao Wei Liewc8537522017-02-05 12:52:35 +08003 * 2017 The LineageOS Project
Steve Kondik75956202016-08-07 23:21:29 -07004 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <errno.h>
19#include <fcntl.h>
20#include <stdbool.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <time.h>
27#include <unistd.h>
28
29#include <cutils/android_reboot.h>
30#include <cutils/klog.h>
31#include <cutils/misc.h>
32#include <cutils/uevent.h>
33#include <cutils/properties.h>
34
35#include <pthread.h>
36#include <linux/android_alarm.h>
37#include <sys/timerfd.h>
38#include <linux/rtc.h>
39
Luca Stefani7d2b6d22016-08-25 14:41:17 +020040#include "healthd/healthd.h"
Steve Kondik75956202016-08-07 23:21:29 -070041#include "minui/minui.h"
42
43#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
44#define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0)
45#define LOGI(x...) do { KLOG_INFO("charger", x); } while (0)
46#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
47
48struct frame {
49 int min_capacity;
50 GRSurface *surface;
51};
52
53struct animation {
54 struct frame *frames;
55 int cur_frame;
56 int num_frames;
57};
58
59static struct animation anim = {
60 .frames = NULL,
61 .cur_frame = 0,
62 .num_frames = 0,
63};
64
65static bool font_inited;
66
67static int draw_surface_centered(GRSurface* surface)
68{
69 int w, h, x, y;
70
71 w = gr_get_width(surface);
72 h = gr_get_height(surface);
73 x = (gr_fb_width() - w) / 2 ;
74 y = (gr_fb_height() - h) / 2 ;
75
76 gr_blit(surface, 0, 0, w, h, x, y);
77 return y + h;
78}
79
80#define STR_LEN 64
81static void draw_capacity(int capacity)
82{
Steve Kondik75956202016-08-07 23:21:29 -070083 char cap_str[STR_LEN];
84 snprintf(cap_str, (STR_LEN - 1), "%d%%", capacity);
Michael W4cc370e2016-08-16 13:06:45 +020085
86 struct frame *f = &anim.frames[0];
87 int font_x, font_y;
88 gr_font_size(&font_x, &font_y);
89 int w = gr_measure(cap_str);
90 int h = gr_get_height(f->surface);
91 int x = (gr_fb_width() - w) / 2;
92 int y = (gr_fb_height() + h) / 2;
93
Steve Kondik75956202016-08-07 23:21:29 -070094 gr_color(255, 255, 255, 255);
Michael W4cc370e2016-08-16 13:06:45 +020095 gr_text(x, y + font_y / 2, cap_str, 0);
Steve Kondik75956202016-08-07 23:21:29 -070096}
97
98#ifdef QCOM_HARDWARE
99enum alarm_time_type {
100 ALARM_TIME,
101 RTC_TIME,
102};
103
104/*
105 * shouldn't be changed after
106 * reading from alarm register
107 */
108static time_t alm_secs;
109
110static int alarm_get_time(enum alarm_time_type time_type,
111 time_t *secs)
112{
113 struct tm tm;
114 unsigned int cmd;
115 int rc, fd = -1;
116
117 if (!secs)
118 return -1;
119
120 fd = open("/dev/rtc0", O_RDONLY);
121 if (fd < 0) {
122 LOGE("Can't open rtc devfs node\n");
123 return -1;
124 }
125
126 switch (time_type) {
127 case ALARM_TIME:
128 cmd = RTC_ALM_READ;
129 break;
130 case RTC_TIME:
131 cmd = RTC_RD_TIME;
132 break;
133 default:
134 LOGE("Invalid time type\n");
135 goto err;
136 }
137
138 rc = ioctl(fd, cmd, &tm);
139 if (rc < 0) {
140 LOGE("Unable to get time\n");
141 goto err;
142 }
143
144 *secs = mktime(&tm) + tm.tm_gmtoff;
145 if (*secs < 0) {
146 LOGE("Invalid seconds = %ld\n", *secs);
147 goto err;
148 }
149
150 close(fd);
151 return 0;
152
153err:
154 close(fd);
155 return -1;
156}
157
158#define ERR_SECS 2
159static int alarm_is_alm_expired()
160{
161 int rc;
162 time_t rtc_secs;
163
164 rc = alarm_get_time(RTC_TIME, &rtc_secs);
165 if (rc < 0)
166 return 0;
167
168 return (alm_secs >= rtc_secs - ERR_SECS &&
169 alm_secs <= rtc_secs + ERR_SECS) ? 1 : 0;
170}
171
172static int timerfd_set_reboot_time_and_wait(time_t secs)
173{
174 int fd;
175 int ret = -1;
176 fd = timerfd_create(CLOCK_REALTIME_ALARM, 0);
177 if (fd < 0) {
178 LOGE("Can't open timerfd alarm node\n");
179 goto err_return;
180 }
181
182 struct itimerspec spec;
183 memset(&spec, 0, sizeof(spec));
184 spec.it_value.tv_sec = secs;
185
186 if (timerfd_settime(fd, 0 /* relative */, &spec, NULL)) {
187 LOGE("Can't set timerfd alarm\n");
188 goto err_close;
189 }
190
191 uint64_t unused;
192 if (read(fd, &unused, sizeof(unused)) < 0) {
193 LOGE("Wait alarm error\n");
194 goto err_close;
195 }
196
197 ret = 0;
198err_close:
199 close(fd);
200err_return:
201 return ret;
202}
203
204static int alarm_set_reboot_time_and_wait(time_t secs)
205{
206 int rc, fd;
207 struct timespec ts;
208
209 fd = open("/dev/alarm", O_RDWR);
210 if (fd < 0) {
211 LOGE("Can't open alarm devfs node, trying timerfd\n");
212 return timerfd_set_reboot_time_and_wait(secs);
213 }
214
215 /* get the elapsed realtime from boot time to now */
216 rc = ioctl(fd, ANDROID_ALARM_GET_TIME(
217 ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP), &ts);
218 if (rc < 0) {
219 LOGE("Unable to get elapsed realtime\n");
220 goto err;
221 }
222
223 /* calculate the elapsed time from boot time to reboot time */
224 ts.tv_sec += secs;
225 ts.tv_nsec = 0;
226
227 rc = ioctl(fd, ANDROID_ALARM_SET(
228 ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP), &ts);
229 if (rc < 0) {
230 LOGE("Unable to set reboot time to %ld\n", secs);
231 goto err;
232 }
233
234 do {
235 rc = ioctl(fd, ANDROID_ALARM_WAIT);
236 } while ((rc < 0 && errno == EINTR) || !alarm_is_alm_expired());
237
238 if (rc <= 0) {
239 LOGE("Unable to wait on alarm\n");
240 goto err;
241 }
242
243 close(fd);
244 return 0;
245
246err:
247 if (fd >= 0)
248 close(fd);
249 return -1;
250}
251
252static void *alarm_thread(void *)
253{
254 time_t rtc_secs, rb_secs;
255 int rc;
256
257 /*
258 * to support power off alarm, the time
259 * stored in alarm register at latest
260 * shutdown time should be some time
261 * earlier than the actual alarm time
262 * set by user
263 */
264 rc = alarm_get_time(ALARM_TIME, &alm_secs);
Adrian DCe91605c2016-08-14 11:40:05 +0200265 LOGI("RTC Alarm %ld\n", alm_secs);
Steve Kondik75956202016-08-07 23:21:29 -0700266 if (rc < 0 || !alm_secs)
267 goto err;
268
269 rc = alarm_get_time(RTC_TIME, &rtc_secs);
Adrian DCe91605c2016-08-14 11:40:05 +0200270 LOGI("RTC Clock %ld\n", rtc_secs);
Steve Kondik75956202016-08-07 23:21:29 -0700271 if (rc < 0)
272 goto err;
273
274 /*
275 * calculate the reboot time after which
276 * the phone will reboot
277 */
278 rb_secs = alm_secs - rtc_secs;
279 if (rb_secs <= 0)
280 goto err;
281
282 rc = alarm_set_reboot_time_and_wait(rb_secs);
283 if (rc < 0)
284 goto err;
285
286 LOGI("Exit from power off charging, reboot the phone!\n");
287 android_reboot(ANDROID_RB_RESTART, 0, 0);
288
289err:
290 LOGE("Exit from alarm thread\n");
291 return NULL;
292}
293#endif
294
295void healthd_board_init(struct healthd_config*)
296{
297 pthread_t tid;
298 char value[PROP_VALUE_MAX];
299 int rc = 0, scale_count = 0, i;
300 GRSurface **scale_frames;
Luca Stefani7d2b6d22016-08-25 14:41:17 +0200301 int scale_fps; // Not in use (charger/cm_battery_scale doesn't have FPS text
302 // chunk). We are using hard-coded frame.disp_time instead.
Steve Kondik75956202016-08-07 23:21:29 -0700303
304 rc = res_create_multi_display_surface("charger/cm_battery_scale",
Zhao Wei Liewc8537522017-02-05 12:52:35 +0800305 &scale_count, &scale_fps, &scale_frames);
Steve Kondik75956202016-08-07 23:21:29 -0700306 if (rc < 0) {
307 LOGE("%s: Unable to load battery scale image", __func__);
308 return;
309 }
310
311 anim.frames = new frame[scale_count];
312 anim.num_frames = scale_count;
313 for (i = 0; i < anim.num_frames; i++) {
314 anim.frames[i].surface = scale_frames[i];
315 anim.frames[i].min_capacity = 100/(scale_count-1) * i;
316 }
317
318#ifdef QCOM_HARDWARE
319 property_get("ro.bootmode", value, "");
320 if (!strcmp("charger", value)) {
321 rc = pthread_create(&tid, NULL, alarm_thread, NULL);
322 if (rc < 0)
323 LOGE("Create alarm thread failed\n");
324 }
325#endif
326}
327
328int healthd_board_battery_update(struct android::BatteryProperties*)
329{
330 // return 0 to log periodic polled battery status to kernel log
331 return 1;
332}
333
334void healthd_board_mode_charger_draw_battery(
335 struct android::BatteryProperties *batt_prop)
336{
337 int start_frame = 0;
338 int capacity = -1;
339
340 if (!font_inited) {
341 gr_set_font("log");
342 font_inited = true;
343 }
344
345 if (batt_prop && batt_prop->batteryLevel >= 0) {
346 capacity = batt_prop->batteryLevel;
347 }
348
349 if (anim.num_frames == 0 || capacity < 0) {
350 LOGE("%s: Unable to draw battery", __func__);
351 return;
352 }
353
354 // Find starting frame to display based on current capacity
355 for (start_frame = 1; start_frame < anim.num_frames; start_frame++) {
356 if (capacity < anim.frames[start_frame].min_capacity)
357 break;
358 }
359 // Always start from the level just below the current capacity
360 start_frame--;
361
362 if (anim.cur_frame < start_frame)
363 anim.cur_frame = start_frame;
364
365 draw_surface_centered(anim.frames[anim.cur_frame].surface);
366 draw_capacity(capacity);
367 // Move to next frame, with max possible frame at max_idx
368 anim.cur_frame = ((anim.cur_frame + 1) % anim.num_frames);
369}
370
371void healthd_board_mode_charger_battery_update(
372 struct android::BatteryProperties*)
373{
374}
375
Adrian DCcadd0be2016-08-14 11:44:31 +0200376#ifdef HEALTHD_BACKLIGHT_PATH
377#ifndef HEALTHD_BACKLIGHT_LEVEL
378#define HEALTHD_BACKLIGHT_LEVEL 100
379#endif
380
381void healthd_board_mode_charger_set_backlight(bool on)
382{
383 int fd;
384 char buffer[10];
385
386 memset(buffer, '\0', sizeof(buffer));
387 fd = open(HEALTHD_BACKLIGHT_PATH, O_RDWR);
388 if (fd < 0) {
389 LOGE("Could not open backlight node : %s\n", strerror(errno));
390 return;
391 }
392 LOGV("Enabling backlight\n");
393 snprintf(buffer, sizeof(buffer), "%d\n", on ? HEALTHD_BACKLIGHT_LEVEL : 0);
394 if (write(fd, buffer, strlen(buffer)) < 0) {
395 LOGE("Could not write to backlight : %s\n", strerror(errno));
396 }
397 close(fd);
398
399#ifdef HEALTHD_SECONDARY_BACKLIGHT_PATH
400 fd = open(HEALTHD_SECONDARY_BACKLIGHT_PATH, O_RDWR);
401 if (fd < 0) {
402 LOGE("Could not open second backlight node : %s\n", strerror(errno));
403 return;
404 }
405 LOGV("Enabling secondary backlight\n");
406 if (write(fd, buffer, strlen(buffer)) < 0) {
407 LOGE("Could not write to second backlight : %s\n", strerror(errno));
408 return;
409 }
410 close(fd);
411#endif
412}
413
414#else
Steve Kondik75956202016-08-07 23:21:29 -0700415void healthd_board_mode_charger_set_backlight(bool)
416{
417}
Adrian DCcadd0be2016-08-14 11:44:31 +0200418#endif
Steve Kondik75956202016-08-07 23:21:29 -0700419
420void healthd_board_mode_charger_init(void)
421{
422}