summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2011-10-21 15:08:40 -0700
committerRandall Spangler <rspangler@chromium.org>2011-10-21 15:14:07 -0700
commite91f32704755c85b5fa54fc4b2bdaa47479c2ffe (patch)
treef91b0e622fb6039f274d5337f25e5143e2c863c4
parent2b3bef79683061e89d3e3c2823c87f592e2f2e23 (diff)
downloadchrome-ec-e91f32704755c85b5fa54fc4b2bdaa47479c2ffe.tar.gz
Add EcOs RTOS abstraction layer and pthreads implementation
This lets us write modules which don't know the details of the RTOS objects they use. More importantly, this lets us target those modules at either Linux (using the pthread-based implementation in chip_stub) or actual hardware, so we can write Linux-based unit tests. BUG=None TEST=make && ./ec_os_test Change-Id: I2923b9a48248b27f3bc3b0346d5a870fef8a1c5b (cherry picked from commit 6ccc37af9fb916f7ad70f542905ba8bacb8e3be2)
-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;
+}