summaryrefslogtreecommitdiff
path: root/core/host/task.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/host/task.c')
-rw-r--r--core/host/task.c208
1 files changed, 208 insertions, 0 deletions
diff --git a/core/host/task.c b/core/host/task.c
new file mode 100644
index 0000000000..caad622778
--- /dev/null
+++ b/core/host/task.c
@@ -0,0 +1,208 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Task scheduling / events module for Chrome EC operating system */
+
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "atomic.h"
+#include "common.h"
+#include "task.h"
+#include "task_id.h"
+#include "timer.h"
+
+struct emu_task_t {
+ pthread_t thread;
+ pthread_cond_t resume;
+ uint32_t event;
+ timestamp_t wake_time;
+};
+
+struct task_args {
+ void (*routine)(void *);
+ void *d;
+};
+
+static struct emu_task_t tasks[TASK_ID_COUNT];
+static pthread_cond_t scheduler_cond;
+static pthread_mutex_t run_lock;
+
+static __thread task_id_t my_task_id; /* thread local task id */
+
+#define TASK(n, r, d, s) void r(void *);
+CONFIG_TASK_LIST
+CONFIG_TEST_TASK_LIST
+#undef TASK
+
+/* Idle task */
+void __idle(void *d)
+{
+ while (1)
+ task_wait_event(-1);
+}
+
+/* Weak reference function as an entry point for unit test */
+test_mockable void run_test(void) { }
+
+void _run_test(void *d)
+{
+ run_test();
+}
+
+#define TASK(n, r, d, s) {r, d},
+struct task_args task_info[TASK_ID_COUNT] = {
+ {__idle, NULL},
+ CONFIG_TASK_LIST
+ CONFIG_TEST_TASK_LIST
+ {_run_test, NULL},
+};
+#undef TASK
+
+#define TASK(n, r, d, s) #n,
+static const char * const task_names[] = {
+ "<< idle >>",
+ CONFIG_TASK_LIST
+ CONFIG_TEST_TASK_LIST
+ "<< test runner >>",
+};
+#undef TASK
+
+void task_pre_init(void)
+{
+ /* Nothing */
+}
+
+int in_interrupt_context(void)
+{
+ return 0; /* No interrupt support yet */
+}
+
+void interrupt_disable(void)
+{
+ /* Not supported yet */
+}
+
+void interrupt_enable(void)
+{
+ /* Not supported yet */
+}
+
+uint32_t task_set_event(task_id_t tskid, uint32_t event, int wait)
+{
+ tasks[tskid].event = event;
+ if (wait)
+ return task_wait_event(-1);
+ return 0;
+}
+
+uint32_t task_wait_event(int timeout_us)
+{
+ int tid = task_get_current();
+ int ret;
+ if (timeout_us > 0)
+ tasks[tid].wake_time.val = get_time().val + timeout_us;
+ pthread_cond_signal(&scheduler_cond);
+ pthread_cond_wait(&tasks[tid].resume, &run_lock);
+ ret = tasks[tid].event;
+ tasks[tid].event = 0;
+ return ret;
+}
+
+void mutex_lock(struct mutex *mtx)
+{
+ int value = 0;
+ int id = 1 << task_get_current();
+
+ mtx->waiters |= id;
+
+ do {
+ if (mtx->lock == 0) {
+ mtx->lock = 1;
+ value = 1;
+ }
+
+ if (!value)
+ task_wait_event(-1);
+ } while (!value);
+
+ mtx->waiters &= ~id;
+}
+
+void mutex_unlock(struct mutex *mtx)
+{
+ int v;
+ mtx->lock = 0;
+
+ for (v = 31; v >= 0; --v)
+ if ((1ul << v) & mtx->waiters) {
+ mtx->waiters &= ~(1ul << v);
+ task_set_event(v, TASK_EVENT_MUTEX, 0);
+ break;
+ }
+}
+
+task_id_t task_get_current(void)
+{
+ return my_task_id;
+}
+
+void task_scheduler(void)
+{
+ int i;
+ timestamp_t now;
+
+ while (1) {
+ now = get_time();
+ i = TASK_ID_COUNT - 1;
+ while (i >= 0) {
+ if (tasks[i].event || now.val >= tasks[i].wake_time.val)
+ break;
+ --i;
+ }
+ if (i < 0)
+ i = TASK_ID_IDLE;
+
+ tasks[i].wake_time.val = ~0ull;
+ pthread_cond_signal(&tasks[i].resume);
+ pthread_cond_wait(&scheduler_cond, &run_lock);
+ }
+}
+
+void *_task_start_impl(void *a)
+{
+ long tid = (long)a;
+ struct task_args *arg = task_info + tid;
+ my_task_id = tid;
+ pthread_mutex_lock(&run_lock);
+ tasks[tid].event = 0;
+ (arg->routine)(arg->d);
+ while (1)
+ task_wait_event(-1);
+}
+
+int task_start(void)
+{
+ int i;
+
+ pthread_mutex_init(&run_lock, NULL);
+ pthread_cond_init(&scheduler_cond, NULL);
+
+ pthread_mutex_lock(&run_lock);
+
+ for (i = 0; i < TASK_ID_COUNT; ++i) {
+ tasks[i].event = TASK_EVENT_WAKE;
+ tasks[i].wake_time.val = ~0ull;
+ pthread_cond_init(&tasks[i].resume, NULL);
+ pthread_create(&tasks[i].thread, NULL, _task_start_impl,
+ (void *)(size_t)i);
+ pthread_cond_wait(&scheduler_cond, &run_lock);
+ }
+
+ task_scheduler();
+
+ return 0;
+}