Index: firmware/export/kernel.h
===================================================================
--- firmware/export/kernel.h	(revision 29752)
+++ firmware/export/kernel.h	(working copy)
@@ -86,6 +86,7 @@
 #define SYS_VOLUME_CHANGED        MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 5)
 
 #define IS_SYSEVENT(ev)           ((ev & SYS_EVENT) == SYS_EVENT)
+#define EVENT_RESERVED            (~0)
 
 #ifndef TIMEOUT_BLOCK
 #define TIMEOUT_BLOCK   -1
@@ -249,6 +250,15 @@
 #endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
 extern bool queue_empty(const struct event_queue* q);
 extern bool queue_peek(struct event_queue *q, struct queue_event *ev);
+
+#define QPEEK_FILTER_COUNT_MASK (0xffu) /* 0x00=1 filter, 0xff=256 filters */
+#define QPEEK_FILTER_HEAD_ONLY  (1u << 8) /* Ignored if no filters */
+#define QPEEK_REMOVE_EVENTS     (1u << 9) /* Remove or discard events */
+extern bool queue_peek_ex(struct event_queue *q,
+                          struct queue_event *ev,
+                          unsigned int flags,
+                          const long (*filters)[2]);
+
 extern void queue_clear(struct event_queue* q);
 extern void queue_remove_from_head(struct event_queue *q, long id);
 extern int queue_count(const struct event_queue *q);
Index: firmware/kernel.c
===================================================================
--- firmware/kernel.c	(revision 29752)
+++ firmware/kernel.c	(working copy)
@@ -516,8 +516,10 @@
     oldlevel = disable_irq_save();
     corelock_lock(&q->cl);
 
-    /* auto-reply */
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+    /* Auto-reply (even if ev is NULL to avoid stalling a waiting thread) */
     queue_do_auto_reply(q->send);
+#endif
 
     while(1)
     {
@@ -541,12 +543,18 @@
         corelock_lock(&q->cl);
     } 
 
-    q->read = rd + 1;
-    rd &= QUEUE_LENGTH_MASK;
-    *ev = q->events[rd];
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+    if(ev)
+#endif
+    {
+        q->read = rd + 1;
+        rd &= QUEUE_LENGTH_MASK;
+        *ev = q->events[rd];
 
-    /* Get data for a waiting thread if one */
-    queue_do_fetch_sender(q->send, rd);
+        /* Get data for a waiting thread if one */
+        queue_do_fetch_sender(q->send, rd);
+    }
+    /* else just waiting on non-empty */
 
     corelock_unlock(&q->cl);
     restore_irq(oldlevel);
@@ -566,8 +574,10 @@
     oldlevel = disable_irq_save();
     corelock_lock(&q->cl);
 
-    /* Auto-reply */
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+    /* Auto-reply (even if ev is NULL to avoid stalling a waiting thread) */
     queue_do_auto_reply(q->send);
+#endif
 
     rd = q->read;
     wr = q->write;
@@ -590,20 +600,26 @@
         wr = q->write;
     }
 
-    /* no worry about a removed message here - status is checked inside
-       locks - perhaps verify if timeout or false alarm */
-    if (rd != wr)
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+    if(ev)
+#endif
     {
-        q->read = rd + 1;
-        rd &= QUEUE_LENGTH_MASK;
-        *ev = q->events[rd];
-        /* Get data for a waiting thread if one */
-        queue_do_fetch_sender(q->send, rd);
+        /* no worry about a removed message here - status is checked inside
+           locks - perhaps verify if timeout or false alarm */
+        if (rd != wr)
+        {
+            q->read = rd + 1;
+            rd &= QUEUE_LENGTH_MASK;
+            *ev = q->events[rd];
+            /* Get data for a waiting thread if one */
+            queue_do_fetch_sender(q->send, rd);
+        }
+        else
+        {
+            ev->id = SYS_TIMEOUT;
+        }
     }
-    else
-    {
-        ev->id = SYS_TIMEOUT;
-    }
+    /* else just waiting on non-empty */
 
     corelock_unlock(&q->cl);
     restore_irq(oldlevel);
@@ -740,23 +756,99 @@
 }
 #endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
 
-bool queue_peek(struct event_queue *q, struct queue_event *ev)
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+/* Scan the even queue from head to tail, returning any event from the
+   filter list that was found, optionally removing the event. If an
+   event is returned, synchronous events are handled in the same manner as
+   with queue_wait(_w_tmo); if discarded, then as queue_clear.
+   If filters are NULL, any event matches. If filters exist, the default
+   is to search the full queue depth.
+   Earlier filters take precedence.
+
+   Return true if an event was found, false otherwise. */
+bool queue_peek_ex(struct event_queue *q, struct queue_event *ev,
+                   unsigned int flags, const long (*filters)[2])
 {
-    unsigned int rd;
+    bool have_msg;
+    unsigned int rd, wr;
+    int oldlevel;
 
-    if(q->read == q->write)
-         return false;
+    if(LIKELY(q->read == q->write))
+        return false; /* Empty: do nothing further */
 
-    bool have_msg = false;
+    have_msg = false;
 
-    int oldlevel = disable_irq_save();
+    oldlevel = disable_irq_save();
     corelock_lock(&q->cl);
 
-    rd = q->read;
-    if(rd != q->write)
+    /* Starting at the head, find first match  */
+    for(rd = q->read, wr = q->write; rd != wr; rd++)
     {
-        *ev = q->events[rd & QUEUE_LENGTH_MASK];
+        struct queue_event *e = &q->events[rd & QUEUE_LENGTH_MASK];
+
+        if(filters)
+        {
+            /* Have filters - find the first thing that passes */
+            const long (* f)[2] = filters;
+            const long (* const f_last)[2] =
+                &filters[flags & QPEEK_FILTER_COUNT_MASK];
+            long id = e->id;
+
+            do
+            {
+                if(UNLIKELY(id >= (*f)[0] && id <= (*f)[1]))
+                    goto passed_filter;
+            }
+            while(++f <= f_last);
+
+            if(LIKELY(!(flags & QPEEK_FILTER_HEAD_ONLY)))
+                continue;   /* No match; test next event */
+            else
+                break;      /* Only check the head */
+        }
+        /* else - anything passes */
+
+    passed_filter:
+
+        /* Found a matching event */
         have_msg = true;
+
+        if(ev)
+            *ev = *e;       /* Caller wants the event */
+
+        if(flags & QPEEK_REMOVE_EVENTS)
+        {
+            /* Do event removal */
+            unsigned int r = q->read;
+            q->read = r + 1; /* Advance head */
+
+            if(ev)
+            {
+                /* Auto-reply */
+                queue_do_auto_reply(q->send);
+                /* Get the thread waiting for reply, if any */
+                queue_do_fetch_sender(q->send, rd & QUEUE_LENGTH_MASK);
+            }
+            else
+            {
+                /* Release any thread waiting on this message */
+                queue_do_unblock_sender(q->send, rd & QUEUE_LENGTH_MASK);
+            }
+
+            /* Slide messages forward into the gap if not at the head */
+            while(rd != r)
+            {
+                unsigned int dst = rd & QUEUE_LENGTH_MASK;
+                unsigned int src = --rd & QUEUE_LENGTH_MASK;
+
+                q->events[dst] = q->events[src];
+                /* Keep sender wait list in sync */
+                if(q->send)
+                    q->send->senders[dst] = q->send->senders[src];
+            }
+        }
+
+        break;
     }
 
     corelock_unlock(&q->cl);
@@ -765,30 +857,42 @@
     return have_msg;
 }
 
-/* Poll queue to see if a message exists - careful in using the result if
- * queue_remove_from_head is called when messages are posted - possibly use
- * queue_wait_w_tmo(&q, 0) in that case or else a removed message that
- * unsignals the queue may cause an unwanted block */
-bool queue_empty(const struct event_queue* q)
+bool queue_peek(struct event_queue *q, struct queue_event *ev)
 {
-    return ( q->read == q->write );
+    return queue_peek_ex(q, ev, 0, NULL);
 }
 
-void queue_clear(struct event_queue* q)
+void queue_remove_from_head(struct event_queue *q, long id)
 {
-    int oldlevel;
+    const long f[2] = { id, id };
+    while (queue_peek_ex(q, NULL,
+            QPEEK_FILTER_HEAD_ONLY | QPEEK_REMOVE_EVENTS, &f));
+}
+#else /* !HAVE_EXTENDED_MESSAGING_AND_NAME */
+/* The more powerful routines aren't required */
+bool queue_peek(struct event_queue *q, struct queue_event *ev)
+{
+    unsigned int rd;
 
-    oldlevel = disable_irq_save();
+    if(q->read == q->write)
+         return false;
+
+    bool have_msg = false;
+
+    int oldlevel = disable_irq_save();
     corelock_lock(&q->cl);
 
-    /* Release all threads waiting in the queue for a reply -
-       dequeued sent message will be handled by owning thread */
-    queue_release_all_senders(q);
+    rd = q->read;
+    if(rd != q->write)
+    {
+        *ev = q->events[rd & QUEUE_LENGTH_MASK];
+        have_msg = true;
+    }
 
-    q->read = q->write;
-
     corelock_unlock(&q->cl);
     restore_irq(oldlevel);
+
+    return have_msg;
 }
 
 void queue_remove_from_head(struct event_queue *q, long id)
@@ -816,7 +920,34 @@
     corelock_unlock(&q->cl);
     restore_irq(oldlevel);
 }
+#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
 
+/* Poll queue to see if a message exists - careful in using the result if
+ * queue_remove_from_head is called when messages are posted - possibly use
+ * queue_wait_w_tmo(&q, 0) in that case or else a removed message that
+ * unsignals the queue may cause an unwanted block */
+bool queue_empty(const struct event_queue* q)
+{
+    return ( q->read == q->write );
+}
+
+void queue_clear(struct event_queue* q)
+{
+    int oldlevel;
+
+    oldlevel = disable_irq_save();
+    corelock_lock(&q->cl);
+
+    /* Release all threads waiting in the queue for a reply -
+       dequeued sent message will be handled by owning thread */
+    queue_release_all_senders(q);
+
+    q->read = q->write;
+
+    corelock_unlock(&q->cl);
+    restore_irq(oldlevel);
+}
+
 /**
  * The number of events waiting in the queue.
  * 
