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