summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--firmware/export/config.h4
-rw-r--r--firmware/export/kernel.h27
-rw-r--r--firmware/export/thread.h7
-rw-r--r--firmware/kernel.c209
-rw-r--r--firmware/thread.c35
-rw-r--r--uisimulator/sdl/kernel.c189
6 files changed, 440 insertions, 31 deletions
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 95e3399690..68a4920d59 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -216,6 +216,10 @@
#endif
#endif
+#if CONFIG_CODEC == SWCODEC && !defined(BOOTLOADER)
+#define HAVE_EXTENDED_MESSAGING_AND_NAME
+#endif
+
#if (CONFIG_CODEC == SWCODEC) && !defined(SIMULATOR) && !defined(BOOTLOADER)
#define HAVE_PRIORITY_SCHEDULING
#define HAVE_SCHEDULER_BOOSTCTRL
diff --git a/firmware/export/kernel.h b/firmware/export/kernel.h
index 2474c6dcac..2a6882955f 100644
--- a/firmware/export/kernel.h
+++ b/firmware/export/kernel.h
@@ -33,7 +33,7 @@
#define QUEUE_LENGTH 16 /* MUST be a power of 2 */
#define QUEUE_LENGTH_MASK (QUEUE_LENGTH - 1)
-/* System defined message ID's, occupying the top 5 bits of the event ID */
+/* System defined message ID's, occupying the top 8 bits of the event ID */
#define SYS_EVENT (long)0x80000000 /* SYS events are negative */
#define SYS_USB_CONNECTED ((SYS_EVENT | ((long)1 << 27)))
#define SYS_USB_CONNECTED_ACK ((SYS_EVENT | ((long)2 << 27)))
@@ -55,12 +55,29 @@ struct event
void *data;
};
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+struct queue_sender
+{
+ struct thread_entry *thread;
+ void *retval;
+};
+
+struct queue_sender_list
+{
+ /* If non-NULL, there is a thread waiting for the corresponding event */
+ struct queue_sender *senders[QUEUE_LENGTH];
+ /* Send info for last message dequeued or NULL if replied or not sent */
+ struct queue_sender *curr_sender;
+};
+#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
+
struct event_queue
{
struct event events[QUEUE_LENGTH];
struct thread_entry *thread;
unsigned int read;
unsigned int write;
+ struct queue_sender_list *send;
};
struct mutex
@@ -90,12 +107,18 @@ int tick_add_task(void (*f)(void));
int tick_remove_task(void (*f)(void));
extern void queue_init(struct event_queue *q, bool register_queue);
+extern void queue_enable_queue_send(struct event_queue *q, struct queue_sender_list *send);
extern void queue_delete(struct event_queue *q);
extern void queue_wait(struct event_queue *q, struct event *ev);
extern void queue_wait_w_tmo(struct event_queue *q, struct event *ev, int ticks);
extern void queue_post(struct event_queue *q, long id, void *data);
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+extern void * queue_send(struct event_queue *q, long id, void *data);
+extern void queue_reply(struct event_queue *q, void *retval);
+extern bool queue_in_queue_send(struct event_queue *q);
+#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
extern bool queue_empty(const struct event_queue* q);
-void queue_clear(struct event_queue* q);
+extern void queue_clear(struct event_queue* q);
extern void queue_remove_from_head(struct event_queue *q, long id);
extern int queue_broadcast(long id, void *data);
diff --git a/firmware/export/thread.h b/firmware/export/thread.h
index 0c1567de97..b079628f23 100644
--- a/firmware/export/thread.h
+++ b/firmware/export/thread.h
@@ -143,6 +143,13 @@ void switch_thread(bool save_context, struct thread_entry **blocked_list);
void sleep_thread(int ticks);
void block_thread(struct thread_entry **thread);
void block_thread_w_tmo(struct thread_entry **thread, int timeout);
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+void set_irq_level_and_block_thread(struct thread_entry **thread, int level);
+#if 0
+void set_irq_level_and_block_thread_w_tmo(struct thread_entry **list,
+ int timeout, int level)
+#endif
+#endif
void wakeup_thread(struct thread_entry **thread);
#ifdef HAVE_PRIORITY_SCHEDULING
int thread_set_priority(struct thread_entry *thread, int priority);
diff --git a/firmware/kernel.c b/firmware/kernel.c
index 5a58935601..55d78e0e0b 100644
--- a/firmware/kernel.c
+++ b/firmware/kernel.c
@@ -86,13 +86,74 @@ void yield(void)
/****************************************************************************
* Queue handling stuff
****************************************************************************/
+
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+/* Moves waiting thread's descriptor to the current sender when a
+ message is dequeued */
+static void queue_fetch_sender(struct queue_sender_list *send,
+ unsigned int i)
+{
+ int old_level = set_irq_level(HIGHEST_IRQ_LEVEL);
+ struct queue_sender **spp = &send->senders[i];
+
+ if(*spp)
+ {
+ send->curr_sender = *spp;
+ *spp = NULL;
+ }
+
+ set_irq_level(old_level);
+}
+
+/* Puts the specified return value in the waiting thread's return value
+ and wakes the thread - a sender should be confirmed to exist first */
+static void queue_release_sender(struct queue_sender **sender, void *retval)
+{
+ (*sender)->retval = retval;
+ wakeup_thread(&(*sender)->thread);
+ *sender = NULL;
+}
+
+/* Releases any waiting threads that are queued with queue_send -
+ reply with NULL */
+static void queue_release_all_senders(struct event_queue *q)
+{
+ if(q->send)
+ {
+ unsigned int i;
+ for(i = q->read; i != q->write; i++)
+ {
+ struct queue_sender **spp =
+ &q->send->senders[i & QUEUE_LENGTH_MASK];
+ if(*spp)
+ {
+ queue_release_sender(spp, NULL);
+ }
+ }
+ }
+}
+
+/* Enables queue_send on the specified queue - caller allocates the extra
+ data structure */
+void queue_enable_queue_send(struct event_queue *q,
+ struct queue_sender_list *send)
+{
+ q->send = send;
+ memset(send, 0, sizeof(*send));
+}
+#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
+
+
void queue_init(struct event_queue *q, bool register_queue)
{
- q->read = 0;
- q->write = 0;
+ q->read = 0;
+ q->write = 0;
q->thread = NULL;
-
- if (register_queue)
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ q->send = NULL; /* No message sending by default */
+#endif
+
+ if(register_queue)
{
/* Add it to the all_queues array */
all_queues[num_queues++] = q;
@@ -118,6 +179,12 @@ void queue_delete(struct event_queue *q)
if(found)
{
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ /* Release waiting threads and reply to any dequeued message
+ waiting for one. */
+ queue_release_all_senders(q);
+ queue_reply(q, NULL);
+#endif
/* Move the following queues up in the list */
for(;i < num_queues-1;i++)
{
@@ -130,24 +197,44 @@ void queue_delete(struct event_queue *q)
void queue_wait(struct event_queue *q, struct event *ev)
{
- if (q->read == q->write)
+ unsigned int rd;
+
+ if(q->read == q->write)
{
block_thread(&q->thread);
}
- *ev = q->events[(q->read++) & QUEUE_LENGTH_MASK];
+ rd = q->read++ & QUEUE_LENGTH_MASK;
+ *ev = q->events[rd];
+
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ if(q->send && q->send->senders[rd])
+ {
+ /* Get data for a waiting thread if one */
+ queue_fetch_sender(q->send, rd);
+ }
+#endif
}
void queue_wait_w_tmo(struct event_queue *q, struct event *ev, int ticks)
{
- if (q->read == q->write && ticks > 0)
+ if(q->read == q->write && ticks > 0)
{
block_thread_w_tmo(&q->thread, ticks);
}
- if (q->read != q->write)
+ if(q->read != q->write)
{
- *ev = q->events[(q->read++) & QUEUE_LENGTH_MASK];
+ unsigned int rd = q->read++ & QUEUE_LENGTH_MASK;
+ *ev = q->events[rd];
+
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ if(q->send && q->send->senders[rd])
+ {
+ /* Get data for a waiting thread if one */
+ queue_fetch_sender(q->send, rd);
+ }
+#endif
}
else
{
@@ -157,20 +244,81 @@ void queue_wait_w_tmo(struct event_queue *q, struct event *ev, int ticks)
void queue_post(struct event_queue *q, long id, void *data)
{
- int wr;
- int oldlevel;
+ int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
+ unsigned int wr = q->write++ & QUEUE_LENGTH_MASK;
- oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
- wr = (q->write++) & QUEUE_LENGTH_MASK;
+ q->events[wr].id = id;
+ q->events[wr].data = data;
+
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ if(q->send)
+ {
+ struct queue_sender **spp = &q->send->senders[wr];
- q->events[wr].id = id;
+ if(*spp)
+ {
+ /* overflow protect - unblock any thread waiting at this index */
+ queue_release_sender(spp, NULL);
+ }
+ }
+#endif
+
+ wakeup_thread(&q->thread);
+ set_irq_level(oldlevel);
+}
+
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+void * queue_send(struct event_queue *q, long id, void *data)
+{
+ int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
+ unsigned int wr = q->write++ & QUEUE_LENGTH_MASK;
+
+ q->events[wr].id = id;
q->events[wr].data = data;
-
+
+ if(q->send)
+ {
+ struct queue_sender **spp = &q->send->senders[wr];
+ struct queue_sender sender;
+
+ if(*spp)
+ {
+ /* overflow protect - unblock any thread waiting at this index */
+ queue_release_sender(spp, NULL);
+ }
+
+ *spp = &sender;
+ sender.thread = NULL;
+
+ wakeup_thread(&q->thread);
+ set_irq_level_and_block_thread(&sender.thread, oldlevel);
+ return sender.retval;
+ }
+
+ /* Function as queue_post if sending is not enabled */
wakeup_thread(&q->thread);
-
set_irq_level(oldlevel);
+ return NULL;
}
+#if 0 /* not used now but probably will be later */
+/* Query if the last message dequeued was added by queue_send or not */
+bool queue_in_queue_send(struct event_queue *q)
+{
+ return q->send && q->send->curr_sender;
+}
+#endif
+
+/* Replies with retval to any dequeued message sent with queue_send */
+void queue_reply(struct event_queue *q, void *retval)
+{
+ if(q->send && q->send->curr_sender)
+ {
+ queue_release_sender(&q->send->curr_sender, retval);
+ }
+}
+#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
+
bool queue_empty(const struct event_queue* q)
{
return ( q->read == q->write );
@@ -179,6 +327,13 @@ bool queue_empty(const struct event_queue* q)
void queue_clear(struct event_queue* q)
{
int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
+
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ /* Release all thread waiting in the queue for a reply -
+ dequeued sent message will be handled by owning thread */
+ queue_release_all_senders(q);
+#endif
+
q->read = 0;
q->write = 0;
set_irq_level(oldlevel);
@@ -188,9 +343,27 @@ void queue_remove_from_head(struct event_queue *q, long id)
{
int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
- while (q->read != q->write &&
- q->events[(q->read) & QUEUE_LENGTH_MASK].id == id)
+ while(q->read != q->write)
{
+ unsigned int rd = q->read & QUEUE_LENGTH_MASK;
+
+ if(q->events[rd].id != id)
+ {
+ break;
+ }
+
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ if(q->send)
+ {
+ struct queue_sender **spp = &q->send->senders[rd];
+
+ if(*spp)
+ {
+ /* Release any thread waiting on this message */
+ queue_release_sender(spp, NULL);
+ }
+ }
+#endif
q->read++;
}
diff --git a/firmware/thread.c b/firmware/thread.c
index 7cff83884d..39880f2a09 100644
--- a/firmware/thread.c
+++ b/firmware/thread.c
@@ -39,6 +39,11 @@ static unsigned short highest_priority IBSS_ATTR;
static int boosted_threads IBSS_ATTR;
#endif
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+#define STAY_IRQ_LEVEL -1
+static int switch_to_irq_level = STAY_IRQ_LEVEL;
+#endif
+
/* Define to enable additional checks for blocking violations etc. */
#define THREAD_EXTRA_CHECKS
@@ -388,6 +393,18 @@ void switch_thread(bool save_context, struct thread_entry **blocked_list)
/* Rearrange thread lists as needed */
change_thread_state(blocked_list);
+
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ /* This has to be done after the scheduler is finished with the
+ blocked_list pointer so that an IRQ can't kill us by attempting
+ a wake but before attempting any core sleep. */
+ if (switch_to_irq_level != STAY_IRQ_LEVEL)
+ {
+ int level = switch_to_irq_level;
+ switch_to_irq_level = STAY_IRQ_LEVEL;
+ set_irq_level(level);
+ }
+#endif
}
/* Go through the list of sleeping task to check if we need to wake up
@@ -471,6 +488,7 @@ void block_thread(struct thread_entry **list)
/* Set the state to blocked and ask the scheduler to switch tasks,
* this takes us off of the run queue until we are explicitly woken */
SET_STATE(current->statearg, STATE_BLOCKED, 0);
+
switch_thread(true, list);
#ifdef HAVE_SCHEDULER_BOOSTCTRL
@@ -522,6 +540,23 @@ void block_thread_w_tmo(struct thread_entry **list, int timeout)
*list = NULL;
}
+#if defined(HAVE_EXTENDED_MESSAGING_AND_NAME) && !defined(SIMULATOR)
+void set_irq_level_and_block_thread(struct thread_entry **list, int level)
+{
+ switch_to_irq_level = level;
+ block_thread(list);
+}
+
+#if 0
+void set_irq_level_and_block_thread_w_tmo(struct thread_entry **list,
+ int timeout, int level)
+{
+ switch_to_irq_level = level;
+ block_thread_w_tmo(list, timeout);
+}
+#endif
+#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
+
void wakeup_thread(struct thread_entry **list)
{
struct thread_entry *thread;
diff --git a/uisimulator/sdl/kernel.c b/uisimulator/sdl/kernel.c
index f1eb095e7b..fa573a3cc9 100644
--- a/uisimulator/sdl/kernel.c
+++ b/uisimulator/sdl/kernel.c
@@ -18,6 +18,7 @@
****************************************************************************/
#include <stdlib.h>
+#include "memory.h"
#include "uisdl.h"
#include "kernel.h"
#include "thread-sdl.h"
@@ -32,13 +33,71 @@ int set_irq_level (int level)
return (_lv = level);
}
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+/* Moves waiting thread's descriptor to the current sender when a
+ message is dequeued */
+static void queue_fetch_sender(struct queue_sender_list *send,
+ unsigned int i)
+{
+ int old_level = set_irq_level(15<<4);
+ struct queue_sender **spp = &send->senders[i];
+
+ if(*spp)
+ {
+ send->curr_sender = *spp;
+ *spp = NULL;
+ }
+
+ set_irq_level(old_level);
+}
+
+/* Puts the specified return value in the waiting thread's return value
+ and wakes the thread - a sender should be confirmed to exist first */
+static void queue_release_sender(struct queue_sender **sender, void *retval)
+{
+ (*sender)->retval = retval;
+ *sender = NULL;
+}
+
+/* Releases any waiting threads that are queued with queue_send -
+ reply with NULL */
+static void queue_release_all_senders(struct event_queue *q)
+{
+ if(q->send)
+ {
+ unsigned int i;
+ for(i = q->read; i != q->write; i++)
+ {
+ struct queue_sender **spp =
+ &q->send->senders[i & QUEUE_LENGTH_MASK];
+ if(*spp)
+ {
+ queue_release_sender(spp, NULL);
+ }
+ }
+ }
+}
+
+/* Enables queue_send on the specified queue - caller allocates the extra
+ data structure */
+void queue_enable_queue_send(struct event_queue *q,
+ struct queue_sender_list *send)
+{
+ q->send = send;
+ memset(send, 0, sizeof(*send));
+}
+#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
+
void queue_init(struct event_queue *q, bool register_queue)
{
(void)register_queue;
- q->read = 0;
- q->write = 0;
+ q->read = 0;
+ q->write = 0;
q->thread = NULL;
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ q->send = NULL; /* No message sending by default */
+#endif
}
void queue_delete(struct event_queue *q)
@@ -48,12 +107,23 @@ void queue_delete(struct event_queue *q)
void queue_wait(struct event_queue *q, struct event *ev)
{
+ unsigned int rd;
+
while(q->read == q->write)
{
switch_thread(true, NULL);
}
- *ev = q->events[(q->read++) & QUEUE_LENGTH_MASK];
+ rd = q->read++ & QUEUE_LENGTH_MASK;
+ *ev = q->events[rd];
+
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ if(q->send && q->send->senders[rd])
+ {
+ /* Get data for a waiting thread if one */
+ queue_fetch_sender(q->send, rd);
+ }
+#endif
}
void queue_wait_w_tmo(struct event_queue *q, struct event *ev, int ticks)
@@ -67,7 +137,16 @@ void queue_wait_w_tmo(struct event_queue *q, struct event *ev, int ticks)
if(q->read != q->write)
{
- *ev = q->events[(q->read++) & QUEUE_LENGTH_MASK];
+ unsigned int rd = q->read++ & QUEUE_LENGTH_MASK;
+ *ev = q->events[rd];
+
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ if(q->send && q->send->senders[rd])
+ {
+ /* Get data for a waiting thread if one */
+ queue_fetch_sender(q->send, rd);
+ }
+#endif
}
else
{
@@ -77,16 +156,81 @@ void queue_wait_w_tmo(struct event_queue *q, struct event *ev, int ticks)
void queue_post(struct event_queue *q, long id, void *data)
{
- int wr;
- int oldlevel;
+ int oldlevel = set_irq_level(15<<4);
+ unsigned int wr = q->write++ & QUEUE_LENGTH_MASK;
+
+ q->events[wr].id = id;
+ q->events[wr].data = data;
+
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ if(q->send)
+ {
+ struct queue_sender **spp = &q->send->senders[wr];
+
+ if(*spp)
+ {
+ /* overflow protect - unblock any thread waiting at this index */
+ queue_release_sender(spp, NULL);
+ }
+ }
+#endif
- oldlevel = set_irq_level(15<<4);
- wr = (q->write++) & QUEUE_LENGTH_MASK;
+ set_irq_level(oldlevel);
+}
- q->events[wr].id = id;
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+void * queue_send(struct event_queue *q, long id, void *data)
+{
+ int oldlevel = set_irq_level(15<<4);
+ unsigned int wr = q->write++ & QUEUE_LENGTH_MASK;
+
+ q->events[wr].id = id;
q->events[wr].data = data;
+
+ if(q->send)
+ {
+ struct queue_sender **spp = &q->send->senders[wr];
+ struct queue_sender sender;
+
+ if(*spp)
+ {
+ /* overflow protect - unblock any thread waiting at this index */
+ queue_release_sender(spp, NULL);
+ }
+
+ *spp = &sender;
+
+ set_irq_level(oldlevel);
+ while (*spp != NULL)
+ {
+ switch_thread(true, NULL);
+ }
+
+ return sender.retval;
+ }
+
+ /* Function as queue_post if sending is not enabled */
set_irq_level(oldlevel);
+ return NULL;
+}
+
+#if 0 /* not used now but probably will be later */
+/* Query if the last message dequeued was added by queue_send or not */
+bool queue_in_queue_send(struct event_queue *q)
+{
+ return q->send && q->send->curr_sender;
}
+#endif
+
+/* Replies with retval to any dequeued message sent with queue_send */
+void queue_reply(struct event_queue *q, void *retval)
+{
+ if(q->send && q->send->curr_sender)
+ {
+ queue_release_sender(&q->send->curr_sender, retval);
+ }
+}
+#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
bool queue_empty(const struct event_queue* q)
{
@@ -96,6 +240,11 @@ bool queue_empty(const struct event_queue* q)
void queue_clear(struct event_queue* q)
{
/* fixme: This is potentially unsafe in case we do interrupt-like processing */
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ /* Release all thread waiting in the queue for a reply -
+ dequeued sent message will be handled by owning thread */
+ queue_release_all_senders(q);
+#endif
q->read = 0;
q->write = 0;
}
@@ -104,9 +253,27 @@ void queue_remove_from_head(struct event_queue *q, long id)
{
int oldlevel = set_irq_level(15<<4);
- while (q->read != q->write &&
- q->events[(q->read) & QUEUE_LENGTH_MASK].id == id)
+ while(q->read != q->write)
{
+ unsigned int rd = q->read & QUEUE_LENGTH_MASK;
+
+ if(q->events[rd].id != id)
+ {
+ break;
+ }
+
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ if(q->send)
+ {
+ struct queue_sender **spp = &q->send->senders[rd];
+
+ if(*spp)
+ {
+ /* Release any thread waiting on this message */
+ queue_release_sender(spp, NULL);
+ }
+ }
+#endif
q->read++;
}