blob: 1eabb5c94c4430acaecedba712c083ec91de274a [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
Steve Kondik75956202016-08-07 23:21:29 -070065static int draw_surface_centered(GRSurface* surface)
66{
67 int w, h, x, y;
68
69 w = gr_get_width(surface);
70 h = gr_get_height(surface);
71 x = (gr_fb_width() - w) / 2 ;
72 y = (gr_fb_height() - h) / 2 ;
73
74 gr_blit(surface, 0, 0, w, h, x, y);
75 return y + h;
76}
77
78#define STR_LEN 64
79static void draw_capacity(int capacity)
80{
Steve Kondik75956202016-08-07 23:21:29 -070081 char cap_str[STR_LEN];
82 snprintf(cap_str, (STR_LEN - 1), "%d%%", capacity);
Michael W4cc370e2016-08-16 13:06:45 +020083
84 struct frame *f = &anim.frames[0];
85 int font_x, font_y;
Dan Pasanen8ee23862017-04-04 13:02:36 -050086 gr_font_size(gr_sys_font(), &font_x, &font_y);
87 int w = gr_measure(gr_sys_font(), cap_str);
Michael W4cc370e2016-08-16 13:06:45 +020088 int h = gr_get_height(f->surface);
89 int x = (gr_fb_width() - w) / 2;
90 int y = (gr_fb_height() + h) / 2;
91
Steve Kondik75956202016-08-07 23:21:29 -070092 gr_color(255, 255, 255, 255);
Dan Pasanen8ee23862017-04-04 13:02:36 -050093 gr_text(gr_sys_font(), x, y + font_y / 2, cap_str, 0);
Steve Kondik75956202016-08-07 23:21:29 -070094}
95
96#ifdef QCOM_HARDWARE
97enum alarm_time_type {
98 ALARM_TIME,
99 RTC_TIME,
100};
101
102/*
103 * shouldn't be changed after
104 * reading from alarm register
105 */
106static time_t alm_secs;
107
108static int alarm_get_time(enum alarm_time_type time_type,
109 time_t *secs)
110{
111 struct tm tm;
112 unsigned int cmd;
113 int rc, fd = -1;
114
115 if (!secs)
116 return -1;
117
118 fd = open("/dev/rtc0", O_RDONLY);
119 if (fd < 0) {
120 LOGE("Can't open rtc devfs node\n");
121 return -1;
122 }
123
124 switch (time_type) {
125 case ALARM_TIME:
126 cmd = RTC_ALM_READ;
127 break;
128 case RTC_TIME:
129 cmd = RTC_RD_TIME;
130 break;
131 default:
132 LOGE("Invalid time type\n");
133 goto err;
134 }
135
136 rc = ioctl(fd, cmd, &tm);
137 if (rc < 0) {
138 LOGE("Unable to get time\n");
139 goto err;
140 }
141
142 *secs = mktime(&tm) + tm.tm_gmtoff;
143 if (*secs < 0) {
144 LOGE("Invalid seconds = %ld\n", *secs);
145 goto err;
146 }
147
148 close(fd);
149 return 0;
150
151err:
152 close(fd);
153 return -1;
154}
155
156#define ERR_SECS 2
157static int alarm_is_alm_expired()
158{
159 int rc;
160 time_t rtc_secs;
161
162 rc = alarm_get_time(RTC_TIME, &rtc_secs);
163 if (rc < 0)
164 return 0;
165
166 return (alm_secs >= rtc_secs - ERR_SECS &&
167 alm_secs <= rtc_secs + ERR_SECS) ? 1 : 0;
168}
169
170static int timerfd_set_reboot_time_and_wait(time_t secs)
171{
172 int fd;
173 int ret = -1;
174 fd = timerfd_create(CLOCK_REALTIME_ALARM, 0);
175 if (fd < 0) {
176 LOGE("Can't open timerfd alarm node\n");
177 goto err_return;
178 }
179
180 struct itimerspec spec;
181 memset(&spec, 0, sizeof(spec));
182 spec.it_value.tv_sec = secs;
183
184 if (timerfd_settime(fd, 0 /* relative */, &spec, NULL)) {
185 LOGE("Can't set timerfd alarm\n");
186 goto err_close;
187 }
188
189 uint64_t unused;
190 if (read(fd, &unused, sizeof(unused)) < 0) {
191 LOGE("Wait alarm error\n");
192 goto err_close;
193 }
194
195 ret = 0;
196err_close:
197 close(fd);
198err_return:
199 return ret;
200}
201
202static int alarm_set_reboot_time_and_wait(time_t secs)
203{
204 int rc, fd;
205 struct timespec ts;
206
207 fd = open("/dev/alarm", O_RDWR);
208 if (fd < 0) {
209 LOGE("Can't open alarm devfs node, trying timerfd\n");
210 return timerfd_set_reboot_time_and_wait(secs);
211 }
212
213 /* get the elapsed realtime from boot time to now */
214 rc = ioctl(fd, ANDROID_ALARM_GET_TIME(
215 ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP), &ts);
216 if (rc < 0) {
217 LOGE("Unable to get elapsed realtime\n");
218 goto err;
219 }
220
221 /* calculate the elapsed time from boot time to reboot time */
222 ts.tv_sec += secs;
223 ts.tv_nsec = 0;
224
225 rc = ioctl(fd, ANDROID_ALARM_SET(
226 ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP), &ts);
227 if (rc < 0) {
228 LOGE("Unable to set reboot time to %ld\n", secs);
229 goto err;
230 }
231
232 do {
233 rc = ioctl(fd, ANDROID_ALARM_WAIT);
234 } while ((rc < 0 && errno == EINTR) || !alarm_is_alm_expired());
235
236 if (rc <= 0) {
237 LOGE("Unable to wait on alarm\n");
238 goto err;
239 }
240
241 close(fd);
242 return 0;
243
244err:
245 if (fd >= 0)
246 close(fd);
247 return -1;
248}
249
250static void *alarm_thread(void *)
251{
252 time_t rtc_secs, rb_secs;
253 int rc;
254
255 /*
256 * to support power off alarm, the time
257 * stored in alarm register at latest
258 * shutdown time should be some time
259 * earlier than the actual alarm time
260 * set by user
261 */
262 rc = alarm_get_time(ALARM_TIME, &alm_secs);
Adrian DCe91605c2016-08-14 11:40:05 +0200263 LOGI("RTC Alarm %ld\n", alm_secs);
Steve Kondik75956202016-08-07 23:21:29 -0700264 if (rc < 0 || !alm_secs)
265 goto err;
266
267 rc = alarm_get_time(RTC_TIME, &rtc_secs);
Adrian DCe91605c2016-08-14 11:40:05 +0200268 LOGI("RTC Clock %ld\n", rtc_secs);
Steve Kondik75956202016-08-07 23:21:29 -0700269 if (rc < 0)
270 goto err;
271
272 /*
273 * calculate the reboot time after which
274 * the phone will reboot
275 */
276 rb_secs = alm_secs - rtc_secs;
277 if (rb_secs <= 0)
278 goto err;
279
280 rc = alarm_set_reboot_time_and_wait(rb_secs);
281 if (rc < 0)
282 goto err;
283
284 LOGI("Exit from power off charging, reboot the phone!\n");
285 android_reboot(ANDROID_RB_RESTART, 0, 0);
286
287err:
288 LOGE("Exit from alarm thread\n");
289 return NULL;
290}
291#endif
292
293void healthd_board_init(struct healthd_config*)
294{
295 pthread_t tid;
296 char value[PROP_VALUE_MAX];
297 int rc = 0, scale_count = 0, i;
298 GRSurface **scale_frames;
Luca Stefani7d2b6d22016-08-25 14:41:17 +0200299 int scale_fps; // Not in use (charger/cm_battery_scale doesn't have FPS text
300 // chunk). We are using hard-coded frame.disp_time instead.
Steve Kondik75956202016-08-07 23:21:29 -0700301
302 rc = res_create_multi_display_surface("charger/cm_battery_scale",
Zhao Wei Liewc8537522017-02-05 12:52:35 +0800303 &scale_count, &scale_fps, &scale_frames);
Steve Kondik75956202016-08-07 23:21:29 -0700304 if (rc < 0) {
305 LOGE("%s: Unable to load battery scale image", __func__);
306 return;
307 }
308
309 anim.frames = new frame[scale_count];
310 anim.num_frames = scale_count;
311 for (i = 0; i < anim.num_frames; i++) {
312 anim.frames[i].surface = scale_frames[i];
313 anim.frames[i].min_capacity = 100/(scale_count-1) * i;
314 }
315
316#ifdef QCOM_HARDWARE
317 property_get("ro.bootmode", value, "");
318 if (!strcmp("charger", value)) {
319 rc = pthread_create(&tid, NULL, alarm_thread, NULL);
320 if (rc < 0)
321 LOGE("Create alarm thread failed\n");
322 }
323#endif
324}
325
326int healthd_board_battery_update(struct android::BatteryProperties*)
327{
328 // return 0 to log periodic polled battery status to kernel log
329 return 1;
330}
331
332void healthd_board_mode_charger_draw_battery(
333 struct android::BatteryProperties *batt_prop)
334{
335 int start_frame = 0;
336 int capacity = -1;
337
Steve Kondik75956202016-08-07 23:21:29 -0700338 if (batt_prop && batt_prop->batteryLevel >= 0) {
339 capacity = batt_prop->batteryLevel;
340 }
341
342 if (anim.num_frames == 0 || capacity < 0) {
343 LOGE("%s: Unable to draw battery", __func__);
344 return;
345 }
346
347 // Find starting frame to display based on current capacity
348 for (start_frame = 1; start_frame < anim.num_frames; start_frame++) {
349 if (capacity < anim.frames[start_frame].min_capacity)
350 break;
351 }
352 // Always start from the level just below the current capacity
353 start_frame--;
354
355 if (anim.cur_frame < start_frame)
356 anim.cur_frame = start_frame;
357
358 draw_surface_centered(anim.frames[anim.cur_frame].surface);
359 draw_capacity(capacity);
360 // Move to next frame, with max possible frame at max_idx
361 anim.cur_frame = ((anim.cur_frame + 1) % anim.num_frames);
362}
363
364void healthd_board_mode_charger_battery_update(
365 struct android::BatteryProperties*)
366{
367}
368
Adrian DCcadd0be2016-08-14 11:44:31 +0200369#ifdef HEALTHD_BACKLIGHT_PATH
370#ifndef HEALTHD_BACKLIGHT_LEVEL
371#define HEALTHD_BACKLIGHT_LEVEL 100
372#endif
373
374void healthd_board_mode_charger_set_backlight(bool on)
375{
376 int fd;
377 char buffer[10];
378
379 memset(buffer, '\0', sizeof(buffer));
380 fd = open(HEALTHD_BACKLIGHT_PATH, O_RDWR);
381 if (fd < 0) {
382 LOGE("Could not open backlight node : %s\n", strerror(errno));
383 return;
384 }
385 LOGV("Enabling backlight\n");
386 snprintf(buffer, sizeof(buffer), "%d\n", on ? HEALTHD_BACKLIGHT_LEVEL : 0);
387 if (write(fd, buffer, strlen(buffer)) < 0) {
388 LOGE("Could not write to backlight : %s\n", strerror(errno));
389 }
390 close(fd);
391
392#ifdef HEALTHD_SECONDARY_BACKLIGHT_PATH
393 fd = open(HEALTHD_SECONDARY_BACKLIGHT_PATH, O_RDWR);
394 if (fd < 0) {
395 LOGE("Could not open second backlight node : %s\n", strerror(errno));
396 return;
397 }
398 LOGV("Enabling secondary backlight\n");
399 if (write(fd, buffer, strlen(buffer)) < 0) {
400 LOGE("Could not write to second backlight : %s\n", strerror(errno));
401 return;
402 }
403 close(fd);
404#endif
405}
406
407#else
Steve Kondik75956202016-08-07 23:21:29 -0700408void healthd_board_mode_charger_set_backlight(bool)
409{
410}
Adrian DCcadd0be2016-08-14 11:44:31 +0200411#endif
Steve Kondik75956202016-08-07 23:21:29 -0700412
413void healthd_board_mode_charger_init(void)
414{
415}