blob: 31ca2686cdb397887e3535db4a2d38b1e1e8930c [file] [log] [blame]
/* alarm management servicest
* (C) Copyright Marvell 2015
* Licensed under the GPLv2
*
* This service makes sure the alarmtimer & RTC wakeup code is
* functioning.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <pthread.h>
#include <assert.h>
#include <sys/time.h>
#include <fcntl.h>
#include <signal.h>
#include <linux/input.h>
#include <errno.h>
#include <asm/ioctl.h>
#include <libubox/blob.h>
#include <libubox/blobmsg.h>
#include <libubox/ustream.h>
#include <libubus.h>
#include <ubusmsg.h>
#include "alarmmanager.h"
#include "aoc.h"
#ifdef CONFIG_ALARM_SERVICE
struct event {
struct timeval it_interval;
unsigned next_value;
int arg;
unsigned short flags;
char alarm_name[32];
struct event *next;
};
static volatile int alarm_update = 0;
static pthread_t alarm_tid;
/* protect access to event list */
static pthread_mutex_t event_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct event *event_queue;
static struct event *event_freelist;
static int g_maxevents;
static int g_events;
static int afd;
static int dev_opened;
static struct blob_buf alarm_buf;
static struct ubus_context *alarm_ctx;
static void check_event_queue();
static void print_event_queue();
static void thread_alarm(void);
size_t strlcpy(char *dst, const char *src, size_t size);
size_t strlcpy(char *dst, const char *src, size_t size)
{
size_t srclen; /* Length of source string */
size --;
srclen = strlen(src);
if (srclen > size)
srclen = size;
memcpy(dst, src, srclen);
dst[srclen] = '\0';
return (srclen);
}
static void alarm_notify_complete_cb(struct ubus_request *req, int ret)
{
(void)(req);
(void)(ret);
return;
}
static int alarm_triggered_notify(struct ubus_context *ctx, struct event *event)
{
struct ubus_notify_request nreq;
if (!event->alarm_name[0])
return 0;
pthread_mutex_lock(&aoc_ubus_mutex);
blobmsg_buf_init(&alarm_buf);
blobmsg_add_string(&alarm_buf, "alarm_name", event->alarm_name);
if (ubus_notify_async(ctx, &aoc_object, "alarm_triggered",
alarm_buf.head, &nreq)) {
pthread_mutex_unlock(&aoc_ubus_mutex);
return 0;
}
blob_buf_free(&alarm_buf);
nreq.complete_cb = (void *)&alarm_notify_complete_cb;
ubus_complete_request(ctx, &nreq.req, 2000);
pthread_mutex_unlock(&aoc_ubus_mutex);
return 0;
}
static struct event *get_event_from_freelist(char *name, struct timeval *tv)
{
struct event *event;
if (!event_freelist)
return NULL;
event = event_freelist;
event_freelist = event_freelist->next;
event->it_interval = *tv;
if (name)
strlcpy(event->alarm_name, name, sizeof(event->alarm_name));
event->next = NULL;
g_events++;
return event;
}
static void put_event_to_freelist(struct event *event)
{
memset(event->alarm_name, 0, sizeof(event->alarm_name));
event->next = event_freelist;
event_freelist = event;
g_events--;
return;
}
static int find_event_and_update(char *name, struct timeval *tv)
{
struct event *event;
pthread_mutex_lock(&event_mutex);
if (event_queue) {
event = event_queue;
while (event) {
if (event->alarm_name[0] &&
!strcmp(event->alarm_name, name)) {
break;
}
event = event->next;
}
if (event) { /* timer already exist */
event->it_interval = *tv;
if (event == event_queue) {
alarm_update = 1;
if (pthread_kill(alarm_tid, SIGUSR1))
ERROR("%s: pthread_kill failed\n", __func__);
}
pthread_mutex_unlock(&event_mutex);
return 1;
}
}
pthread_mutex_unlock(&event_mutex);
return 0;
}
static int queue_event_to_waitlist(char *name, struct timeval *tv)
{
struct event *event = NULL;
struct event *prev_ev, *cur_ev;
int ret;
pthread_mutex_lock(&event_mutex);
event = get_event_from_freelist(name, tv);
if (!event) {
pthread_mutex_unlock(&event_mutex);
return 1;
}
if (event_queue) {
prev_ev = cur_ev = event_queue;
while (cur_ev) {
if (timercmp(tv, &(cur_ev->it_interval), <)) {
break;
}
prev_ev = cur_ev;
cur_ev = cur_ev->next;
}
if (cur_ev != event_queue) {
DEBUG(2, "New alarm will be inserted into event_queue.\n");
event->next = prev_ev->next;
prev_ev->next = event;
} else {
DEBUG(2, "Set new alarm into top of event_queue,\
and set new alarm.\n");
event->next = event_queue;
event_queue = event;
alarm_update = 1;
if (pthread_kill(alarm_tid, SIGUSR1))
ERROR("%s: pthread_kill failed\n", __func__);
}
print_event_queue();
check_event_queue();
} else { /* event_queue empty */
event_queue = event;
event_queue->flags = TFLAG_NONE;
event_queue->next = NULL;
/* if more than 1 event, monitor thread */
ret = pthread_create(&alarm_tid, NULL, (void *)thread_alarm, NULL);
if (ret) {
ERROR("[%s] pthread_create error\n", __FUNCTION__);
pthread_mutex_unlock(&event_mutex);
return 0;
}
}
pthread_mutex_unlock(&event_mutex);
return 0;
}
static int check_elapsed_event_from_waitlist(struct timeval *tv,
struct timespec *ts)
{
struct event *event = NULL;
int ret;
pthread_mutex_lock(&event_mutex);
if (!timercmp(tv, &(event_queue->it_interval), >)) {
pthread_mutex_unlock(&event_mutex);
return 0;
}
event = event_queue;
event_queue= event_queue->next;
alarm_triggered_notify(alarm_ctx, event);
put_event_to_freelist(event);
if (event_queue) {
print_event_queue();
TIMEVAL_TO_TIMESPEC(&(event_queue->it_interval), ts);
ret = 2;
} else {
ret = 1;
}
pthread_mutex_unlock(&event_mutex);
return ret;
}
static int mmp_timer_settime(int alarm_delay, char *name)
{
struct timespec ts;
struct timeval tv;
int ret;
if (!dev_opened) {
afd = open("/dev/alarm", O_RDWR);
if (afd < 0) {
ERROR("Unable to open rtc: %s\n", strerror(errno));
return 1;
}
dev_opened = 1;
}
ret = ioctl(afd, ANDROID_ALARM_GET_TIME(ANDROID_ALARM_RTC_WAKEUP), &ts);
if (ret < 0) {
ERROR("Unable to get current time: %s\n", strerror(errno));
return 1;
}
ts.tv_sec += alarm_delay;
TIMESPEC_TO_TIMEVAL(&tv, &ts);
if (find_event_and_update(name, &tv))
return 0;
if (queue_event_to_waitlist(name, &tv))
return 1;
return 0;
}
static int mmp_cancel_timer(char *name)
{
struct event *event = NULL;
struct event *prev;
pthread_mutex_lock(&event_mutex);
if (!dev_opened || !event_queue) {
pthread_mutex_unlock(&event_mutex);
return 0;
}
prev = event = event_queue;
while (event) {
if (event->alarm_name[0] &&
!strcmp(name, event->alarm_name)) {
DEBUG(2, "mmp_cancel_timer:find alarm_name=%s\n", name);
break;
}
prev = event;
event = event->next;
}
if (!event) {
DEBUG(2, "Not found timer: %s\n", name);
pthread_mutex_unlock(&event_mutex);
return 0;
}
if (event == event_queue) {
event_queue = event_queue->next;
print_event_queue();
if (event_queue)
alarm_update = 1;
if (pthread_kill(alarm_tid, SIGUSR1))
ERROR("%s: pthread_kill failed\n", __func__);
} else {
prev->next = event->next;
}
put_event_to_freelist(event);
pthread_mutex_unlock(&event_mutex);
return 0;
}
static void check_event_queue()
{
struct event *event;
int i = 0;
for (event = event_queue; event; event = event->next) {
if (i > g_maxevents) {
DEBUG(2, "timer queue is full!");
print_event_queue();
exit(1);
}
i++;
}
}
static void print_event_queue()
{
struct event *event;
int i = 0;
for (event = event_queue; event; event = event->next) {
DEBUG(2, "#%d %s: (0x%x)->0x%x: \t%d sec %d usec\n",
i++, event->alarm_name ? event->alarm_name : "noname",
(unsigned int) event, (unsigned int) event->next,
(int) event->it_interval.tv_sec,
(int) event->it_interval.tv_usec);
if (i > g_maxevents) {
DEBUG(2, "...(giving up)\n");
break;
}
}
}
static void alarm_sighandler(int signo)
{
struct timespec ts;
int ret;
DEBUG(2, "alarm_sighandler: signo=%d\n", signo);
if (alarm_update) {
alarm_update = 0;
TIMEVAL_TO_TIMESPEC(&(event_queue->it_interval), &ts);
ret = ioctl(afd, ANDROID_ALARM_SET(ANDROID_ALARM_RTC_WAKEUP),
&ts);
if (ret < 0)
ERROR("Unable to set alarm: %s\n", strerror(errno));
}
}
static void thread_alarm(void)
{
struct timespec ts;
struct timeval tv;
struct sigaction actoins;
int ret;
pthread_detach(pthread_self());
memset(&actoins, 0, sizeof(actoins));
sigemptyset(&actoins.sa_mask);
actoins.sa_flags = 0;
actoins.sa_handler = alarm_sighandler;
sigaction(SIGUSR1, &actoins, NULL);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
TIMEVAL_TO_TIMESPEC(&(event_queue->it_interval), &ts);
ret = ioctl(afd, ANDROID_ALARM_SET(ANDROID_ALARM_RTC_WAKEUP), &ts);
if (ret < 0) {
ERROR("Unable to set alarm: %s\n", strerror(errno));
return;
}
while (true) {
ret = ioctl(afd, ANDROID_ALARM_WAIT);
if(ret < 0)
DEBUG(2, "alarm wait exit due to signal\n");
else
DEBUG(2, "got alarm %x\n", ret);
if (!event_queue)
goto out;
ret = ioctl(afd,
ANDROID_ALARM_GET_TIME(ANDROID_ALARM_RTC_WAKEUP), &ts);
if (ret < 0)
ERROR("Unable to get current time: %s\n",
strerror(errno));
TIMESPEC_TO_TIMEVAL(&tv, &ts);
ret = check_elapsed_event_from_waitlist(&tv, &ts);
if (ret == 2) {
ret = ioctl(afd,
ANDROID_ALARM_SET(ANDROID_ALARM_RTC_WAKEUP),
&ts);
if (ret < 0)
ERROR("Unable to set alarm: %s\n",
strerror(errno));
} else if (ret == 1) {
/* only 1 event, thread should kill self.*/
goto out;
}
}
out:
pthread_exit((void *)0);
}
int alarm_set(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__ALARM_MAX];
int alarm_time;
int ret;
char *alarm_name = NULL;
(void)obj;
(void)method;
alarm_time = 0;
ret = blobmsg_parse(alarm_policy, __ALARM_MAX, tb, blob_data(msg),
blob_len(msg));
if (ret) {
ERROR("[%s] parsing blobmsg failed %d\n",
__FUNCTION__, ret);
return -UBUS_STATUS_INVALID_ARGUMENT;
}
if (tb[ALARM_TIME])
alarm_time = blobmsg_get_u32(tb[ALARM_TIME]);
if (tb[ALARM_NAME])
alarm_name = blobmsg_get_string(tb[ALARM_NAME]);
DEBUG(2, "---------[%d]-----------\n", alarm_time);
if (alarm_time > 0)
ret = mmp_timer_settime(alarm_time, alarm_name);
else {
DEBUG(2, "----error-----[%d]-----------\n", alarm_time);
ret = 1;
}
blobmsg_buf_init(&alarm_buf);
blobmsg_add_u32(&alarm_buf, "rc", ret);
ubus_send_reply(ctx, req, alarm_buf.head);
blob_buf_free(&alarm_buf);
return 0;
}
int alarm_cancel(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__ALARM_MAX];
int ret;
char *alarm_name = NULL;
(void)obj;
(void)method;
ret = blobmsg_parse(alarm_policy, __ALARM_MAX, tb, blob_data(msg),
blob_len(msg));
if (ret) {
ERROR("[%s] parsing blobmsg failed %d\n",
__FUNCTION__, ret);
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (tb[ALARM_NAME]) {
alarm_name = blobmsg_get_string(tb[ALARM_NAME]);
ret = mmp_cancel_timer(alarm_name);
}
blobmsg_buf_init(&alarm_buf);
blobmsg_add_u32(&alarm_buf, "rc", ret);
ubus_send_reply(ctx, req, alarm_buf.head);
blob_buf_free(&alarm_buf);
return 0;
}
void alarm_init(struct ubus_context *ctx)
{
int i;
alarm_ctx = ctx;
g_events = 0;
dev_opened = 0;
g_maxevents = MAX_EVENT;
event_freelist = (struct event *) malloc(MAX_EVENT * sizeof(struct event));
memset(event_freelist, 0, MAX_EVENT * sizeof(struct event));
for (i = 0; i < (MAX_EVENT-1); i++)
event_freelist[i].next = &event_freelist[i+1];
event_freelist[i].next = NULL;
event_queue = NULL;
DEBUG(2, "Alarmmanager service running up ...\n");
}
#endif //#ifdef CONFIG_ALARM_SERVICE