diff options
Diffstat (limited to 'healthd/healthd_board_msm.cpp')
-rw-r--r-- | healthd/healthd_board_msm.cpp | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/healthd/healthd_board_msm.cpp b/healthd/healthd_board_msm.cpp new file mode 100644 index 0000000..d17095f --- /dev/null +++ b/healthd/healthd_board_msm.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +#include <cutils/android_reboot.h> +#include <cutils/klog.h> +#include <cutils/misc.h> +#include <cutils/uevent.h> +#include <cutils/properties.h> + +#include <pthread.h> +#include <linux/android_alarm.h> +#include <sys/timerfd.h> +#include <linux/rtc.h> + +#include <healthd.h> + +#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0) +#define LOGI(x...) do { KLOG_INFO("charger", x); } while (0) +#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0) + +enum alarm_time_type { + ALARM_TIME, + RTC_TIME, +}; + +/* + * shouldn't be changed after + * reading from alarm register + */ +static time_t alm_secs; + +static int alarm_get_time(enum alarm_time_type time_type, + time_t *secs) +{ + struct tm tm; + unsigned int cmd; + int rc, fd = -1; + + if (!secs) + return -1; + + fd = open("/dev/rtc0", O_RDONLY); + if (fd < 0) { + LOGE("Can't open rtc devfs node\n"); + return -1; + } + + switch (time_type) { + case ALARM_TIME: + cmd = RTC_ALM_READ; + break; + case RTC_TIME: + cmd = RTC_RD_TIME; + break; + default: + LOGE("Invalid time type\n"); + goto err; + } + + rc = ioctl(fd, cmd, &tm); + if (rc < 0) { + LOGE("Unable to get time\n"); + goto err; + } + + *secs = mktime(&tm) + tm.tm_gmtoff; + if (*secs < 0) { + LOGE("Invalid seconds = %ld\n", *secs); + goto err; + } + + close(fd); + return 0; + +err: + close(fd); + return -1; +} + +#define ERR_SECS 2 +static int alarm_is_alm_expired() +{ + int rc; + time_t rtc_secs; + + rc = alarm_get_time(RTC_TIME, &rtc_secs); + if (rc < 0) + return 0; + + return (alm_secs >= rtc_secs - ERR_SECS && + alm_secs <= rtc_secs + ERR_SECS) ? 1 : 0; +} + +static int timerfd_set_reboot_time_and_wait(time_t secs) +{ + int fd; + int ret = -1; + fd = timerfd_create(CLOCK_REALTIME_ALARM, 0); + if (fd < 0) { + LOGE("Can't open timerfd alarm node\n"); + goto err_return; + } + + struct itimerspec spec; + memset(&spec, 0, sizeof(spec)); + spec.it_value.tv_sec = secs; + + if (timerfd_settime(fd, 0 /* relative */, &spec, NULL)) { + LOGE("Can't set timerfd alarm\n"); + goto err_close; + } + + uint64_t unused; + if (read(fd, &unused, sizeof(unused)) < 0) { + LOGE("Wait alarm error\n"); + goto err_close; + } + + ret = 0; +err_close: + close(fd); +err_return: + return ret; +} + +static int alarm_set_reboot_time_and_wait(time_t secs) +{ + int rc, fd; + struct timespec ts; + + fd = open("/dev/alarm", O_RDWR); + if (fd < 0) { + LOGE("Can't open alarm devfs node, trying timerfd\n"); + return timerfd_set_reboot_time_and_wait(secs); + } + + /* get the elapsed realtime from boot time to now */ + rc = ioctl(fd, ANDROID_ALARM_GET_TIME( + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP), &ts); + if (rc < 0) { + LOGE("Unable to get elapsed realtime\n"); + goto err; + } + + /* calculate the elapsed time from boot time to reboot time */ + ts.tv_sec += secs; + ts.tv_nsec = 0; + + rc = ioctl(fd, ANDROID_ALARM_SET( + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP), &ts); + if (rc < 0) { + LOGE("Unable to set reboot time to %ld\n", secs); + goto err; + } + + do { + rc = ioctl(fd, ANDROID_ALARM_WAIT); + } while ((rc < 0 && errno == EINTR) || !alarm_is_alm_expired()); + + if (rc <= 0) { + LOGE("Unable to wait on alarm\n"); + goto err; + } + + close(fd); + return 0; + +err: + if (fd >= 0) + close(fd); + return -1; +} + +static void *alarm_thread(void *) +{ + time_t rtc_secs, rb_secs; + int rc; + + /* + * to support power off alarm, the time + * stored in alarm register at latest + * shutdown time should be some time + * earlier than the actual alarm time + * set by user + */ + rc = alarm_get_time(ALARM_TIME, &alm_secs); + if (rc < 0 || !alm_secs) + goto err; + + rc = alarm_get_time(RTC_TIME, &rtc_secs); + if (rc < 0) + goto err; + + /* + * calculate the reboot time after which + * the phone will reboot + */ + rb_secs = alm_secs - rtc_secs; + if (rb_secs <= 0) + goto err; + + rc = alarm_set_reboot_time_and_wait(rb_secs); + if (rc < 0) + goto err; + + LOGI("Exit from power off charging, reboot the phone!\n"); + android_reboot(ANDROID_RB_RESTART, 0, 0); + +err: + LOGE("Exit from alarm thread\n"); + return NULL; +} + +void healthd_board_init(struct healthd_config*) +{ + pthread_t tid; + int rc; + char value[PROP_VALUE_MAX]; + + property_get("ro.bootmode", value, ""); + if (!strcmp("charger", value)) { + rc = pthread_create(&tid, NULL, alarm_thread, NULL); + if (rc < 0) + LOGE("Create alarm thread failed\n"); + } +} + +int healthd_board_battery_update(struct android::BatteryProperties*) +{ + // return 0 to log periodic polled battery status to kernel log + return 1; +} + +void healthd_board_mode_charger_draw_battery(struct android::BatteryProperties*) +{ + +} + +void healthd_board_mode_charger_battery_update(struct android::BatteryProperties*) +{ + +} + +void healthd_board_mode_charger_set_backlight(bool) +{ + +} + +void healthd_board_mode_charger_init() +{ + +} |