ASR_BASE
Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/services/aoc/alarmmanager.c b/marvell/services/aoc/alarmmanager.c
new file mode 100644
index 0000000..31ca268
--- /dev/null
+++ b/marvell/services/aoc/alarmmanager.c
@@ -0,0 +1,526 @@
+/* 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