summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2011-10-24 10:46:04 -0700
committerGerrit Code Review <gerrit@gerrit.golo.chromium.org>2011-10-24 10:46:04 -0700
commit059b4334719577821e2cac0c2bfa90ca4552d622 (patch)
tree5b65c4085b66ad952775ddbae8710f652e6349e4
parent84c5a86d241b05ac9c76cad30600416f9a21f9e6 (diff)
parente91f32704755c85b5fa54fc4b2bdaa47479c2ffe (diff)
downloadchrome-ec-059b4334719577821e2cac0c2bfa90ca4552d622.tar.gz
Merge "Add EcOs RTOS abstraction layer and pthreads implementation"
-rw-r--r--cros_ec/Makefile15
-rw-r--r--cros_ec/chip_stub/ec_os.c516
-rw-r--r--cros_ec/chip_stub/ec_os_types.h93
-rw-r--r--cros_ec/include/ec_common.h2
-rw-r--r--cros_ec/include/ec_os.h119
-rw-r--r--cros_ec/test/ec_os_test.c129
6 files changed, 871 insertions, 3 deletions
diff --git a/cros_ec/Makefile b/cros_ec/Makefile
index ff59ee3b50..f34db019c3 100644
--- a/cros_ec/Makefile
+++ b/cros_ec/Makefile
@@ -6,13 +6,22 @@ LIB_SRCS=\
lib/ec_console.c
STUB_SRCS=\
+ chip_stub/ec_os.c \
chip_stub/ec_uart.c
-all: fakemain
+TESTPROGS=fakemain ec_os_test
+
+CFLAGS=-Wall -I include -I chip_stub -pthread
+
+all: $(TESTPROGS)
clean:
- rm -f fakemain
+ rm -f $(TESTPROGS)
+
+ec_os_test: test/ec_os_test.c chip_stub/ec_os.c
+ gcc $(CFLAGS) -o ec_os_test \
+ test/ec_os_test.c chip_stub/ec_os.c
fakemain: test/fakemain.c $(LIB_SRCS) $(STUB_SRCS)
- gcc -Wall -I include -o fakemain test/fakemain.c \
+ gcc $(CFLAGS) -o fakemain test/fakemain.c \
$(LIB_SRCS) $(STUB_SRCS)
diff --git a/cros_ec/chip_stub/ec_os.c b/cros_ec/chip_stub/ec_os.c
new file mode 100644
index 0000000000..1997bf9111
--- /dev/null
+++ b/cros_ec/chip_stub/ec_os.c
@@ -0,0 +1,516 @@
+/* Copyright (c) 2011 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.
+ */
+
+/* Operating system library EC */
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "ec_common.h"
+#include "ec_os.h"
+
+
+static int os_has_started = 0;
+static pthread_mutex_t os_start_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t os_start_cond = PTHREAD_COND_INITIALIZER;
+
+
+/* Waits for OS to start */
+static void WaitForOsStart(void) {
+ pthread_mutex_lock(&os_start_mutex);
+ if (!os_has_started)
+ pthread_cond_wait(&os_start_cond, &os_start_mutex);
+ pthread_mutex_unlock(&os_start_mutex);
+}
+
+static void UsecToTimespec(int usec, struct timespec* ts) {
+ ts->tv_sec = usec / 1000000;
+ ts->tv_nsec = 1000 * (long)(usec % 1000000);
+}
+
+/*****************************************************************************/
+/* Tasks */
+
+/* Internal data for a task */
+typedef struct EcTaskInternal {
+ pthread_t thread;
+ void (*task_func)(void*);
+ void* param;
+ struct EcTaskInternal* next;
+} EcTaskInternal;
+
+
+static EcTaskInternal* task_list = NULL;
+static pthread_mutex_t task_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+
+/* Task wrapper. Waits for OS to start, then runs the task function. */
+static void* EcTaskWrapper(void* param) {
+ EcTaskInternal* ti = (EcTaskInternal*)param;
+
+ WaitForOsStart();
+
+ /* Chain to the task function */
+ ti->task_func(ti->param);
+
+ return NULL;
+}
+
+
+EcError EcTaskCreate(EcTask* task, int priority, int stack_size,
+ void (*task_func)(void*), void* param) {
+ EcTaskInternal* ti = (EcTaskInternal*)task;
+ pthread_attr_t attr;
+
+ /* TODO: priority */
+
+ /* Initialize task data */
+ ti->task_func = task_func;
+ ti->param = param;
+
+ /* Add it to the task list */
+ pthread_mutex_lock(&task_list_mutex);
+ ti->next = task_list;
+ task_list = ti;
+ pthread_mutex_unlock(&task_list_mutex);
+
+ /* Mark thread as joinable */
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ /* Create the thread */
+ if (pthread_create(&ti->thread, &attr, EcTaskWrapper, ti) != 0)
+ return EC_ERROR_UNKNOWN;
+
+ return EC_SUCCESS;
+}
+
+
+void EcTaskSleep(int usec) {
+ usleep(usec);
+}
+
+
+void EcTaskExit(void) {
+ pthread_exit(NULL);
+}
+
+
+/*****************************************************************************/
+/* Software interrupts (SWI)
+ *
+ * SWIs don't exist in pthreads. Simulate them with a thread which waits
+ * on a semaphore and calls the SWI function when it wakes. */
+
+typedef struct EcSwiInternal {
+ pthread_t thread;
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ uint32_t pending_bits;
+ void (*swi_func)(void *, uint32_t);
+ void* param;
+} EcSwiInternal;
+
+
+/* SWI wrapper. Loops and calls SWI function when semaphore is signalled. */
+static void* EcSwiWrapper(void* param) {
+ EcSwiInternal* si = (EcSwiInternal*)param;
+
+ WaitForOsStart();
+
+ while (1) {
+ int bits;
+
+ pthread_mutex_lock(&si->mutex);
+ pthread_cond_wait(&si->cond, &si->mutex);
+ bits = si->pending_bits;
+ si->pending_bits = 0;
+ pthread_mutex_unlock(&si->mutex);
+
+ if (bits)
+ si->swi_func(si->param, bits);
+ }
+
+ return NULL;
+}
+
+
+EcError EcSwiCreate(EcSwi* swi, int priority,
+ void (*swi_func)(void*, uint32_t), void* param) {
+ EcSwiInternal* si = (EcSwiInternal*)swi;
+
+ /* TODO: priority */
+
+ /* Init internal data */
+ si->pending_bits = 0;
+ si->swi_func = swi_func;
+ si->param = param;
+
+ /* Allocate pthreads objects for the swi */
+ if (pthread_mutex_init(&si->mutex, NULL) != 0)
+ return EC_ERROR_UNKNOWN;
+ if (pthread_cond_init(&si->cond, NULL) != 0)
+ return EC_ERROR_UNKNOWN;
+ if (pthread_create(&si->thread, NULL, EcSwiWrapper, si) != 0)
+ return EC_ERROR_UNKNOWN;
+
+ return EC_SUCCESS;
+}
+
+
+/* Sets the specified bits in the SWI. */
+EcError EcSwiPost(EcSwi* swi, uint32_t bits) {
+ EcSwiInternal* si = (EcSwiInternal*)swi;
+ int prev_bits;
+
+ pthread_mutex_lock(&si->mutex);
+
+ prev_bits = si->pending_bits;
+ si->pending_bits |= bits;
+
+ if (!prev_bits)
+ pthread_cond_signal(&si->cond);
+
+ pthread_mutex_unlock(&si->mutex);
+
+ return EC_SUCCESS;
+}
+
+
+/*****************************************************************************/
+/* Timers */
+
+
+typedef struct EcTimerInternal {
+ pthread_t thread;
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ int interval_usec;
+ int flags;
+ void (*timer_func)(void *);
+ void* param;
+} EcTimerInternal;
+
+/* Timer flags */
+/* Timer is periodic; if not present, timer is one-shot. */
+#define EC_TIMER_FLAG_PERIODIC 0x01
+#define EC_TIMER_FLAG_STARTED 0x02
+
+
+/* Timer wrapper. Loops and calls timer function. */
+static void* EcTimerWrapper(void* param) {
+ EcTimerInternal* ti = (EcTimerInternal*)param;
+
+ WaitForOsStart();
+
+ while (1) {
+
+ /* Wait for timer to be enabled */
+ pthread_mutex_lock(&ti->mutex);
+ if (!(ti->flags & EC_TIMER_FLAG_STARTED))
+ pthread_cond_wait(&ti->cond, &ti->mutex);
+ pthread_mutex_unlock(&ti->mutex);
+
+ /* TODO: should really sleep for interval, less the time used by
+ * the previous call. Or we could use a second thread to
+ * pthread_cond_signal() and then immediately go back to sleep. */
+ usleep(ti->interval_usec);
+
+ /* Only call the timer func if the flag is still started */
+ if (ti->flags & EC_TIMER_FLAG_STARTED)
+ ti->timer_func(ti->param);
+
+ if (!(ti->flags & EC_TIMER_FLAG_PERIODIC))
+ break; /* One-shot timer */
+ }
+
+ return NULL;
+}
+
+
+/* Creates a timer which will call timer_func(param) after the
+ * specified interval. See EC_TIMER_FLAG_* for valid flags. Fills
+ * <timer>. */
+EcError EcTimerCreate(EcTimer* timer, int interval_usec, int priority,
+ uint32_t flags, void (*timer_func)(void*), void* param) {
+ EcTimerInternal* ti = (EcTimerInternal*)timer;
+
+ /* TODO: priority */
+
+ /* Init internal data */
+ ti->interval_usec = interval_usec;
+ ti->flags = flags;
+ ti->timer_func = timer_func;
+ ti->param = param;
+
+ /* Create thread to call timer func */
+ if (pthread_mutex_init(&ti->mutex, NULL) != 0)
+ return EC_ERROR_UNKNOWN;
+ if (pthread_cond_init(&ti->cond, NULL) != 0)
+ return EC_ERROR_UNKNOWN;
+ if (pthread_create(&ti->thread, NULL, EcTimerWrapper, ti) != 0)
+ return EC_ERROR_UNKNOWN;
+
+ return EC_SUCCESS;
+}
+
+
+/* Stops a timer. */
+EcError EcTimerStop(EcTimer* timer) {
+ EcTimerInternal* ti = (EcTimerInternal*)timer;
+
+ pthread_mutex_lock(&ti->mutex);
+ ti->flags &= ~EC_TIMER_FLAG_STARTED;
+ pthread_mutex_unlock(&ti->mutex);
+ return EC_SUCCESS;
+}
+
+
+/* Starts a timer. */
+EcError EcTimerStart(EcTimer* timer) {
+ EcTimerInternal* ti = (EcTimerInternal*)timer;
+
+ pthread_mutex_lock(&ti->mutex);
+ ti->flags |= EC_TIMER_FLAG_STARTED;
+ pthread_cond_signal(&ti->cond);
+ pthread_mutex_unlock(&ti->mutex);
+ return EC_SUCCESS;
+}
+
+
+/*****************************************************************************/
+/* Semaphores */
+
+typedef struct EcSemaphoreInternal {
+ sem_t sem;
+} EcSemaphoreInternal;
+
+
+EcError EcSemaphoreCreate(EcSemaphore* semaphore, int initial_count) {
+ EcSemaphoreInternal* si = (EcSemaphoreInternal*)semaphore;
+
+ if (sem_init(&si->sem, 0, initial_count) != 0)
+ return EC_ERROR_UNKNOWN;
+
+ return EC_SUCCESS;
+}
+
+
+EcError EcSemaphorePost(EcSemaphore* semaphore) {
+ EcSemaphoreInternal* si = (EcSemaphoreInternal*)semaphore;
+
+ if (sem_post(&si->sem) != 0)
+ return EC_ERROR_UNKNOWN;
+
+ return EC_SUCCESS;
+}
+
+
+EcError EcSemaphoreWait(EcSemaphore* semaphore, int timeout_usec) {
+ EcSemaphoreInternal* si = (EcSemaphoreInternal*)semaphore;
+ int rv;
+
+ if (timeout_usec == 0) {
+ rv = sem_trywait(&si->sem);
+
+ } else if (timeout_usec == EC_OS_FOREVER) {
+ rv = sem_wait(&si->sem);
+ if (errno == EAGAIN)
+ return EC_ERROR_TIMEOUT;
+
+ } else {
+ struct timespec ts;
+ UsecToTimespec(timeout_usec, &ts);
+ rv = sem_timedwait(&si->sem, &ts);
+ if (errno == ETIMEDOUT)
+ return EC_ERROR_TIMEOUT;
+ }
+
+ return (rv == 0 ? EC_SUCCESS : EC_ERROR_UNKNOWN);
+}
+
+
+EcError EcSemaphoreGetCount(EcSemaphore* semaphore, int* count_ptr) {
+ EcSemaphoreInternal* si = (EcSemaphoreInternal*)semaphore;
+
+ if (sem_getvalue(&si->sem, count_ptr) != 0)
+ return EC_ERROR_UNKNOWN;
+
+ return EC_SUCCESS;
+}
+
+/*****************************************************************************/
+/* Events */
+
+typedef struct EcEventInternal {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ uint32_t bits_set;
+ uint32_t bits_or;
+ uint32_t bits_and;
+} EcEventInternal;
+
+EcError EcEventCreate(EcEvent* event, uint32_t initial_bits) {
+ EcEventInternal* ei = (EcEventInternal*)event;
+
+ /* Init internal data */
+ ei->bits_set = initial_bits;
+ ei->bits_or = ei->bits_and = 0;
+ if (pthread_mutex_init(&ei->mutex, NULL) != 0)
+ return EC_ERROR_UNKNOWN;
+ if (pthread_cond_init(&ei->cond, NULL) != 0)
+ return EC_ERROR_UNKNOWN;
+
+ return EC_SUCCESS;
+}
+
+/* Turns on the specified bits in the event. */
+EcError EcEventPost(EcEvent* event, uint32_t bits) {
+ EcEventInternal* ei = (EcEventInternal*)event;
+
+ pthread_mutex_lock(&ei->mutex);
+
+ ei->bits_set |= bits;
+
+ /* See if that's enough bits to release the thread waiting on us */
+ if (ei->bits_or & ei->bits_set) {
+ ei->bits_or = 0;
+ pthread_cond_signal(&ei->cond);
+
+ } else if (ei->bits_and && (ei->bits_and & ei->bits_set) == ei->bits_and) {
+ ei->bits_and = 0;
+ pthread_cond_signal(&ei->cond);
+ }
+
+ pthread_mutex_unlock(&ei->mutex);
+ return EC_SUCCESS;
+}
+
+
+EcError EcEventWaitAll(EcEvent* event, uint32_t bits, int timeout_usec) {
+ EcEventInternal* ei = (EcEventInternal*)event;
+ int rv;
+
+ pthread_mutex_lock(&ei->mutex);
+
+ /* Only wait if we don't have the bits we need */
+ if ((ei->bits_set & bits) != bits) {
+ ei->bits_and = bits;
+
+ if (timeout_usec == EC_OS_FOREVER) {
+ rv = pthread_cond_wait(&ei->cond, &ei->mutex);
+ } else {
+ struct timespec ts;
+ UsecToTimespec(timeout_usec, &ts);
+ rv = pthread_cond_timedwait(&ei->cond, &ei->mutex, &ts);
+ }
+ }
+
+ /* If we succeeded, consume all the bits we waited for */
+ if (!rv)
+ ei->bits_set &= ~bits;
+
+ pthread_mutex_unlock(&ei->mutex);
+
+ if (rv == ETIMEDOUT)
+ return EC_ERROR_TIMEOUT;
+ else
+ return (rv == 0 ? EC_SUCCESS : EC_ERROR_UNKNOWN);
+}
+
+
+EcError EcEventWaitAny(EcEvent* event, uint32_t bits, uint32_t* got_bits_ptr,
+ int timeout_usec) {
+ EcEventInternal* ei = (EcEventInternal*)event;
+ int rv;
+
+ pthread_mutex_lock(&ei->mutex);
+
+ /* Only wait if we don't have the bits we need */
+ if (!(ei->bits_set & bits)) {
+ ei->bits_or = bits;
+
+ if (timeout_usec == EC_OS_FOREVER) {
+ rv = pthread_cond_wait(&ei->cond, &ei->mutex);
+ } else {
+ struct timespec ts;
+ UsecToTimespec(timeout_usec, &ts);
+ rv = pthread_cond_timedwait(&ei->cond, &ei->mutex, &ts);
+ }
+ }
+
+ /* If we succeeded, consume all the bits we waited for */
+ if (!rv) {
+ if (got_bits_ptr)
+ *got_bits_ptr = ei->bits_set & bits;
+ ei->bits_set &= ~bits;
+ }
+
+ pthread_mutex_unlock(&ei->mutex);
+
+ if (rv == ETIMEDOUT)
+ return EC_ERROR_TIMEOUT;
+ else
+ return (rv == 0 ? EC_SUCCESS : EC_ERROR_UNKNOWN);
+}
+
+/*****************************************************************************/
+/* Other functions */
+
+
+void EcOsInit(void) {
+
+ /* Make sure struct sizes are correct */
+ //printf("%ld", sizeof(EcTimerInternal));
+ assert(EC_TASK_STRUCT_SIZE == sizeof(EcTaskInternal));
+ assert(EC_SWI_STRUCT_SIZE == sizeof(EcSwiInternal));
+ assert(EC_TIMER_STRUCT_SIZE == sizeof(EcTimerInternal));
+ assert(EC_SEMAPHORE_STRUCT_SIZE == sizeof(EcSemaphoreInternal));
+ assert(EC_EVENT_STRUCT_SIZE == sizeof(EcEventInternal));
+}
+
+
+void EcOsStart(void) {
+ EcTaskInternal* ti;
+
+ /* Kick off threads */
+ pthread_mutex_lock(&os_start_mutex);
+ os_has_started = 1;
+ pthread_cond_broadcast(&os_start_cond);
+ pthread_mutex_unlock(&os_start_mutex);
+
+ /* Wait for all task threads to run */
+ while (1) {
+ EcTaskInternal* ti_wait;
+
+ /* Find the next task */
+ pthread_mutex_lock(&task_list_mutex);
+ ti_wait = task_list;
+ pthread_mutex_unlock(&task_list_mutex);
+ if (!ti_wait)
+ break; /* No tasks left */
+
+ /* Wait for it to die */
+ pthread_join(ti_wait->thread, NULL);
+
+ /* Remove the dead thread from the list */
+ pthread_mutex_lock(&task_list_mutex);
+ if (task_list == ti_wait)
+ task_list = ti_wait->next;
+ else {
+ for (ti = task_list; ti->next == ti_wait; ti = ti->next);
+ if (ti)
+ ti->next = ti_wait->next;
+ }
+ pthread_mutex_unlock(&task_list_mutex);
+ }
+
+ /* The remaining tasks for SWIs, etc, will die when the process exits */
+}
diff --git a/cros_ec/chip_stub/ec_os_types.h b/cros_ec/chip_stub/ec_os_types.h
new file mode 100644
index 0000000000..f9c4a263e0
--- /dev/null
+++ b/cros_ec/chip_stub/ec_os_types.h
@@ -0,0 +1,93 @@
+/* Copyright (c) 2011 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.
+ */
+
+/* Operating system object types for EC. These are
+ * implementation-dependent; this file should come from the
+ * implementation include directory. */
+
+#ifndef __CROS_EC_OS_TYPES_H
+#define __CROS_EC_OS_TYPES_H
+
+#include "ec_common.h"
+
+/* Structure sizes depend on the underlying implementation. These
+ * sizes are correct for the pthreads implementation. */
+#define EC_TASK_STRUCT_SIZE 32
+#define EC_SWI_STRUCT_SIZE 120
+#define EC_TIMER_STRUCT_SIZE 120
+#define EC_SEMAPHORE_STRUCT_SIZE 32
+#define EC_EVENT_STRUCT_SIZE 104
+
+/*****************************************************************************/
+/* Tasks */
+
+/* Task priority range */
+#define EC_TASK_PRIORITY_LOWEST 0
+#define EC_TASK_PRIORITY_DEFAULT 3
+#define EC_TASK_PRIORITY_HIGHEST 7
+
+/* Task instance. Treat this as an opaque identifier. */
+typedef struct EcTask {
+ union {
+ uint64_t align; /* Align on something big */
+ uint8_t data[EC_TASK_STRUCT_SIZE];
+ };
+} EcTask;
+
+/*****************************************************************************/
+/* Software interrupts (SWI) */
+
+/* SWI priority range */
+#define EC_SWI_PRIORITY_LOWEST 0
+#define EC_SWI_PRIORITY_DEFAULT 3
+#define EC_SWI_PRIORITY_HIGHEST 7
+
+/* SWI instance. Treat this as an opaque identifier. */
+typedef struct EcSwi {
+ union {
+ uint64_t align; /* Align on something big */
+ uint8_t data[EC_SWI_STRUCT_SIZE];
+ };
+} EcSwi;
+
+/*****************************************************************************/
+/* Timers */
+
+/* Timer priority range */
+#define EC_TIMER_PRIORITY_LOWEST 0
+#define EC_TIMER_PRIORITY_DEFAULT 3
+#define EC_TIMER_PRIORITY_HIGHEST 7
+
+/* Timer instance. Treat this as an opaque identifier. */
+typedef struct EcTimer {
+ union {
+ uint64_t align; /* Align on something big */
+ uint8_t data[EC_TIMER_STRUCT_SIZE];
+ };
+} EcTimer;
+
+/*****************************************************************************/
+/* Semaphores */
+
+/* Semaphore instance. Treat this as an opaque identifier. */
+typedef struct EcSemaphore {
+ union {
+ uint64_t align; /* Align on something big */
+ uint8_t data[EC_SEMAPHORE_STRUCT_SIZE];
+ };
+} EcSemaphore;
+
+/*****************************************************************************/
+/* Events */
+
+/* Event instance. Treat this as an opaque identifier. */
+typedef struct EcEvent {
+ union {
+ uint64_t align; /* Align on something big */
+ uint8_t data[EC_EVENT_STRUCT_SIZE];
+ };
+} EcEvent;
+
+#endif
diff --git a/cros_ec/include/ec_common.h b/cros_ec/include/ec_common.h
index 615a88adcf..d5adb4e280 100644
--- a/cros_ec/include/ec_common.h
+++ b/cros_ec/include/ec_common.h
@@ -25,6 +25,8 @@ enum EcErrorList {
EC_ERROR_UNIMPLEMENTED = 2,
/* Overflow error; too much input provided. */
EC_ERROR_OVERFLOW = 3,
+ /* Timeout */
+ EC_ERROR_TIMEOUT = 4,
/* Module-internal error codes may use this range. */
EC_ERROR_INTERNAL_FIRST = 0x10000,
diff --git a/cros_ec/include/ec_os.h b/cros_ec/include/ec_os.h
new file mode 100644
index 0000000000..b21e4772cd
--- /dev/null
+++ b/cros_ec/include/ec_os.h
@@ -0,0 +1,119 @@
+/* Copyright (c) 2011 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.
+ */
+
+/* Operating system objects for EC */
+
+#ifndef __CROS_EC_OS_H
+#define __CROS_EC_OS_H
+
+#include "ec_common.h"
+#include "ec_os_types.h"
+
+/* Timeout value which means "wait forever". */
+#define EC_OS_FOREVER -1
+
+/*****************************************************************************/
+/* Tasks */
+
+/* Creates a task of the specified priority and stack size. If
+ * stack_size=0, uses the default stack size. The task will call
+ * task_func(param). Fills <task>. */
+EcError EcTaskCreate(EcTask* task, int priority, int stack_size,
+ void (*task_func)(void*), void* param);
+
+/* Sleep for the specified number of microseconds. */
+void EcTaskSleep(int usec);
+
+/* Exits the current task. */
+void EcTaskExit(void);
+
+/*****************************************************************************/
+/* Software interrupts (SWI) */
+
+/* Creates a SWI of the specified priority. When the SWI is
+ * triggered, it will call swi_func(param, bits), where <bits> is the
+ * accumulated bits value from all preceding calls to EcSwiPost().
+ * Fills <swi>. */
+EcError EcSwiCreate(EcSwi* swi, int priority,
+ void (*swi_func)(void*, uint32_t), void* param);
+
+/* Sets the specified bits in the SWI. */
+EcError EcSwiPost(EcSwi* swi, uint32_t bits);
+
+/*****************************************************************************/
+/* Timers */
+
+/* Timer flags */
+/* Timer is periodic; if not present, timer is one-shot. */
+#define EC_TIMER_FLAG_PERIODIC 0x01
+#define EC_TIMER_FLAG_STARTED 0x02
+
+/* Creates a timer which will call timer_func(param) after the
+ * specified interval. See EC_TIMER_FLAG_* for valid flags. Fills
+ * <timer>. */
+EcError EcTimerCreate(EcTimer* timer, int interval_usec, int priority,
+ uint32_t flags, void (*timer_func)(void*), void* param);
+
+/* Stops a timer. */
+EcError EcTimerStop(EcTimer* timer);
+
+/* Starts a timer. */
+EcError EcTimerStart(EcTimer* timer);
+
+/*****************************************************************************/
+/* Semaphores */
+
+/* Creates a semaphore with the specified initial count. Fills <semaphore>. */
+EcError EcSemaphoreCreate(EcSemaphore* semaphore, int initial_count);
+
+/* Posts the semaphore, incrementing its count by one. If count>0,
+ * this will allow the next task pending on the semaphore to run. */
+EcError EcSemaphorePost(EcSemaphore* semaphore);
+
+/* Waits up to <timeout_usec> microseconds (or forever, if
+ * timeout_usec==EC_OS_FOREVER) for the semaphore. If it's unable to
+ * acquire the semaphore before the timeout, returns
+ * EC_ERROR_TIMEOUT. */
+EcError EcSemaphoreWait(EcSemaphore* semaphore, int timeout_usec);
+
+/* Stores the current semaphore count into <count_ptr>. */
+EcError EcSemaphoreGetCount(EcSemaphore* semaphore, int* count_ptr);
+
+/*****************************************************************************/
+/* Events
+ *
+ * To be compatible with all platforms, only one task at a time may
+ * wait on an event. */
+
+/* Creates an event with the specified initial bits. Fills <event>. */
+EcError EcEventCreate(EcEvent* event, uint32_t initial_bits);
+
+/* Turns on the specified bits in the event. */
+EcError EcEventPost(EcEvent* event, uint32_t bits);
+
+/* Waits up to <timeout_usec> microseconds (or forever, if
+ * timeout_usec==EC_OS_FOREVER) for all of the requested bits to be
+ * set in the event. Returns EC_ERROR_TIMEOUT on timeout. */
+EcError EcEventWaitAll(EcEvent* event, uint32_t bits, int timeout_usec);
+
+/* Waits up to <timeout_usec> microseconds (or forever, if
+ * timeout_usec==EC_OS_FOREVER) for any of the requested bits to be
+ * set in the event. If got_bits_ptr!=NULL, sets it to the bits which
+ * were posted, and clears those bits. Returns EC_ERROR_TIMEOUT on timeout. */
+EcError EcEventWaitAny(EcEvent* event, uint32_t bits, uint32_t* got_bits_ptr,
+ int timeout_usec);
+
+/*****************************************************************************/
+/* Other OS functions */
+
+/* Initializes the OS. Must be called before any of the functions above. */
+void EcOsInit(void);
+
+/* Starts OS task management. Returns when all threads have exited.
+ * This function should be called by main(). */
+void EcOsStart(void);
+
+
+#endif
diff --git a/cros_ec/test/ec_os_test.c b/cros_ec/test/ec_os_test.c
new file mode 100644
index 0000000000..f34ccec4d3
--- /dev/null
+++ b/cros_ec/test/ec_os_test.c
@@ -0,0 +1,129 @@
+/* Copyright (c) 2011 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.
+ */
+
+/* Basic test for EcOs objects */
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "ec_os.h"
+
+EcTask t1, t2, t3, t4;
+EcSemaphore sem;
+EcSwi swi;
+EcTimer timer1, timer2;
+EcEvent ev1, ev2;
+
+
+void Thread1(void* arg) {
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ EcSemaphoreWait(&sem, EC_OS_FOREVER);
+ /* Do some work */
+ EcTaskSleep(5000);
+ fprintf(stderr, "Hello from thread1: %s\n", (char*)arg);
+ EcSemaphorePost(&sem);
+
+ /* Two rapid posts to SWI, to see that they merge */
+ EcSwiPost(&swi, 1 << i);
+ EcSwiPost(&swi, 0x100 << i);
+
+ EcTaskSleep(100);
+ }
+
+ EcTaskSleep(500000);
+ fprintf(stderr, "Goodbye from thread1\n");
+}
+
+
+void Thread2(void* arg) {
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ EcSemaphoreWait(&sem, EC_OS_FOREVER);
+ /* Do some work */
+ EcTaskSleep(5000);
+ fprintf(stderr, "Hello from thread2: %s\n", (char*)arg);
+ EcSemaphorePost(&sem);
+
+ /* Post events */
+ EcEventPost(&ev1, 1 << i);
+ EcEventPost(&ev2, 1 << i);
+
+ EcTaskSleep(100);
+ }
+
+ EcTaskSleep(50000);
+ fprintf(stderr, "Goodbye from thread2\n");
+}
+
+
+void Thread3(void* arg) {
+ uint32_t got_bits = 0;
+
+ while(got_bits != 0x10) {
+ /* Wait for any of the bits to be set */
+
+ EcEventWaitAny(&ev1, 0x1c, &got_bits, EC_OS_FOREVER);
+ fprintf(stderr, "Event thread 3 got bits: 0x%x\n", got_bits);
+ }
+ fprintf(stderr, "Goodbye from event thread 3\n");
+}
+
+
+void Thread4(void* arg) {
+ /* Wait on event bit from creation and a few posted bits. */
+ EcEventWaitAll(&ev2, 0x10e, EC_OS_FOREVER);
+ fprintf(stderr, "Event thread 4 got all bits\n");
+ fprintf(stderr, "Goodbye from event thread 4\n");
+}
+
+
+void SwiFunc(void* arg, uint32_t bits) {
+ fprintf(stderr, "Hello from SWI with bits=0x%x\n", bits);
+}
+
+
+void TimerFunc(void* arg) {
+ fprintf(stderr, "Hello from timer: %s\n", (char*)arg);
+ /* Start the one-shot timer. */
+ EcTimerStart(&timer2);
+}
+
+
+void OneTimerFunc(void* arg) {
+ fprintf(stderr, "Hello from one-shot timer: %s\n", (char*)arg);
+ /* Stop the periodic timer */
+ EcTimerStop(&timer1);
+}
+
+
+int main(void) {
+ fprintf(stderr, "Hello, world.\n");
+
+ EcOsInit();
+
+ EcTaskCreate(&t1, EC_TASK_PRIORITY_DEFAULT, 0, Thread1, "Foo1");
+ EcTaskCreate(&t2, EC_TASK_PRIORITY_DEFAULT, 0, Thread2, "Foo2");
+ EcTaskCreate(&t3, EC_TASK_PRIORITY_DEFAULT, 0, Thread3, "EventTask1");
+ EcTaskCreate(&t4, EC_TASK_PRIORITY_DEFAULT, 0, Thread4, "EventTask2");
+
+ EcSwiCreate(&swi, EC_SWI_PRIORITY_DEFAULT, SwiFunc, "Swi1");
+ EcTimerCreate(&timer1, 100000, EC_TIMER_PRIORITY_DEFAULT,
+ EC_TIMER_FLAG_STARTED|EC_TIMER_FLAG_PERIODIC,
+ TimerFunc, "Timer1");
+ EcTimerCreate(&timer2, 150000, EC_TIMER_PRIORITY_DEFAULT,
+ 0, OneTimerFunc, "Timer2");
+ EcSemaphoreCreate(&sem, 1);
+ EcEventCreate(&ev1, 0);
+ EcEventCreate(&ev2, 0x100);
+
+ fprintf(stderr, "EcOs objects created.\n");
+
+ EcOsStart();
+
+ return 0;
+}