You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by an...@apache.org on 2021/10/08 14:51:25 UTC

[mynewt-nimble] branch master updated: nimble/ll: Refactor item scheduling

This is an automated email from the ASF dual-hosted git repository.

andk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git


The following commit(s) were added to refs/heads/master by this push:
     new fbd0a8c  nimble/ll: Refactor item scheduling
fbd0a8c is described below

commit fbd0a8c24d369a17dc04281f58a17ed7392e7f22
Author: Andrzej Kaczmarek <an...@codecoup.pl>
AuthorDate: Tue Sep 28 01:38:26 2021 +0200

    nimble/ll: Refactor item scheduling
    
    This refactors all scheduling APIs (i.e. insertions to scheduler) to
    use common code instead of reimplementing the same or similar flow
    over and over again. The single 'ble_ll_sched_insert' call covers all
    currently possible scenarios with minimal overhead so all existing
    insertion APIs use it and add some more extra processing if needed.
    Also it adds some flexibility via preemption callback so it's possible
    to better define priorities for each item.
---
 .../controller/include/controller/ble_ll_sched.h   |    9 +-
 nimble/controller/src/ble_ll_adv.c                 |   22 +-
 nimble/controller/src/ble_ll_conn.c                |    4 +-
 nimble/controller/src/ble_ll_sched.c               | 1083 ++++++--------------
 4 files changed, 352 insertions(+), 766 deletions(-)

diff --git a/nimble/controller/include/controller/ble_ll_sched.h b/nimble/controller/include/controller/ble_ll_sched.h
index bed8640..15f3319 100644
--- a/nimble/controller/include/controller/ble_ll_sched.h
+++ b/nimble/controller/include/controller/ble_ll_sched.h
@@ -25,8 +25,8 @@ extern "C" {
 #endif
 
 /* Time per BLE scheduler slot */
-#define BLE_LL_SCHED_USECS_PER_SLOT         (1250)
-#define BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT   (41)    /* 1 tick = 30.517 usecs */
+#define BLE_LL_SCHED_USECS_PER_SLOT   (1250)
+#define BLE_LL_SCHED_TICKS_PER_SLOT   (41)    /* 1 tick = 30.517 usecs */
 
 /*
  * Worst case time needed for scheduled advertising item. This is the longest
@@ -160,8 +160,7 @@ int ble_ll_sched_adv_new(struct ble_ll_sched_item *sch,
                          ble_ll_sched_adv_new_cb cb, void *arg);
 
 /* Schedule periodic advertising event */
-int ble_ll_sched_periodic_adv(struct ble_ll_sched_item *sch, uint32_t *start,
-                              bool after_overlap);
+int ble_ll_sched_periodic_adv(struct ble_ll_sched_item *sch, bool first_event);
 
 int ble_ll_sched_sync_reschedule(struct ble_ll_sched_item *sch,
                                  uint32_t anchor_point,
@@ -172,7 +171,7 @@ int ble_ll_sched_sync(struct ble_ll_sched_item *sch,
                       int8_t phy_mode);
 
 /* Reschedule an advertising event */
-int ble_ll_sched_adv_reschedule(struct ble_ll_sched_item *sch, uint32_t *start,
+int ble_ll_sched_adv_reschedule(struct ble_ll_sched_item *sch,
                                 uint32_t max_delay_ticks);
 
 /* Reschedule and advertising pdu */
diff --git a/nimble/controller/src/ble_ll_adv.c b/nimble/controller/src/ble_ll_adv.c
index 7efe914..19270ac 100644
--- a/nimble/controller/src/ble_ll_adv.c
+++ b/nimble/controller/src/ble_ll_adv.c
@@ -2280,7 +2280,6 @@ ble_ll_adv_periodic_schedule_first(struct ble_ll_adv_sm *advsm,
 {
     struct ble_ll_adv_sync *sync;
     struct ble_ll_sched_item *sch;
-    uint32_t sch_start;
     uint32_t max_usecs;
     uint8_t chan;
     int rc;
@@ -2323,7 +2322,7 @@ ble_ll_adv_periodic_schedule_first(struct ble_ll_adv_sm *advsm,
     sch->end_time = sch->start_time + ble_ll_usecs_to_ticks_round_up(max_usecs);
     sch->start_time -= g_ble_ll_sched_offset_ticks;
 
-    rc = ble_ll_sched_periodic_adv(sch, &sch_start, first_pdu);
+    rc = ble_ll_sched_periodic_adv(sch, first_pdu);
     if (rc) {
         STATS_INC(ble_ll_stats, periodic_adv_drop_event);
         ble_npl_eventq_put(&g_ble_ll_data.ll_evq,
@@ -2331,7 +2330,7 @@ ble_ll_adv_periodic_schedule_first(struct ble_ll_adv_sm *advsm,
         return;
     }
 
-    sync->start_time = sch_start + g_ble_ll_sched_offset_ticks;
+    sync->start_time = sch->start_time + g_ble_ll_sched_offset_ticks;
 
     assert(first_pdu ||
            (sync->start_time == advsm->periodic_adv_event_start_time));
@@ -4585,13 +4584,15 @@ ble_ll_adv_drop_event(struct ble_ll_adv_sm *advsm)
 static void
 ble_ll_adv_reschedule_event(struct ble_ll_adv_sm *advsm)
 {
-    int rc;
-    uint32_t start_time;
+    struct ble_ll_sched_item *sch;
     uint32_t max_delay_ticks;
+    int rc;
 
     assert(advsm->adv_enabled);
 
-    if (!advsm->adv_sch.enqueued) {
+    sch = &advsm->adv_sch;
+
+    if (!sch->enqueued) {
         if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
             max_delay_ticks = 0;
         } else {
@@ -4599,16 +4600,15 @@ ble_ll_adv_reschedule_event(struct ble_ll_adv_sm *advsm)
                     os_cputime_usecs_to_ticks(BLE_LL_ADV_DELAY_MS_MAX * 1000);
         }
 
-        rc = ble_ll_sched_adv_reschedule(&advsm->adv_sch, &start_time,
-                                         max_delay_ticks);
+        rc = ble_ll_sched_adv_reschedule(sch, max_delay_ticks);
         if (rc) {
             ble_ll_adv_drop_event(advsm);
             return;
         }
 
-        start_time += g_ble_ll_sched_offset_ticks;
-        advsm->adv_event_start_time = start_time;
-        advsm->adv_pdu_start_time = start_time;
+        advsm->adv_event_start_time = sch->start_time +
+                                      g_ble_ll_sched_offset_ticks;
+        advsm->adv_pdu_start_time = advsm->adv_event_start_time;
     }
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
diff --git a/nimble/controller/src/ble_ll_conn.c b/nimble/controller/src/ble_ll_conn.c
index a2a577e..0f930d9 100644
--- a/nimble/controller/src/ble_ll_conn.c
+++ b/nimble/controller/src/ble_ll_conn.c
@@ -2337,7 +2337,7 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm)
 #if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
     itvl = g_ble_ll_sched_data.sch_ticks_per_period;
 #else
-    itvl = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT;
+    itvl = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_TICKS_PER_SLOT;
 #endif
     if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
 
@@ -2451,7 +2451,7 @@ ble_ll_conn_created(struct ble_ll_conn_sm *connsm, struct ble_mbuf_hdr *rxhdr)
 
 #else
         connsm->ce_end_time = connsm->anchor_point +
-            (MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT)
+            (MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_TICKS_PER_SLOT)
             + os_cputime_usecs_to_ticks(connsm->slave_cur_tx_win_usecs) + 1;
 #endif
         connsm->slave_cur_window_widening = BLE_LL_JITTER_USECS;
diff --git a/nimble/controller/src/ble_ll_sched.c b/nimble/controller/src/ble_ll_sched.c
index b0fc4f5..1eb7d54 100644
--- a/nimble/controller/src/ble_ll_sched.c
+++ b/nimble/controller/src/ble_ll_sched.c
@@ -20,7 +20,6 @@
 #include <stdlib.h>
 #include <assert.h>
 #include <string.h>
-#include "os/os.h"
 #include "os/os_cputime.h"
 #include "ble/xcvr.h"
 #include "controller/ble_phy.h"
@@ -35,20 +34,21 @@
 #include "ble_ll_priv.h"
 #include "ble_ll_conn_priv.h"
 
-/* XXX: this is temporary. Not sure what I want to do here */
-struct hal_timer g_ble_ll_sched_timer;
+#define BLE_LL_SCHED_MAX_DELAY_ANY      (0x7fffffff)
 
-uint8_t g_ble_ll_sched_offset_ticks;
+static struct hal_timer g_ble_ll_sched_timer;
+static uint8_t g_ble_ll_sched_timer_running;
 
-#define BLE_LL_SCHED_ADV_WORST_CASE_USECS       \
-    (BLE_LL_SCHED_MAX_ADV_PDU_USECS + BLE_LL_IFS + BLE_LL_SCHED_ADV_MAX_USECS \
-     + XCVR_TX_SCHED_DELAY_USECS)
+uint8_t g_ble_ll_sched_offset_ticks;
 
 #if (BLE_LL_SCHED_DEBUG == 1)
 int32_t g_ble_ll_sched_max_late;
 int32_t g_ble_ll_sched_max_early;
 #endif
 
+typedef int (* ble_ll_sched_preempt_cb_t)(struct ble_ll_sched_item *sch,
+                                          struct ble_ll_sched_item *item);
+
 /* XXX: TODO:
  *  1) Add some accounting to the schedule code to see how late we are
  *  (min/max?)
@@ -62,12 +62,203 @@ int32_t g_ble_ll_sched_max_early;
  */
 
 /* Queue for timers */
-TAILQ_HEAD(ll_sched_qhead, ble_ll_sched_item) g_ble_ll_sched_q;
+static TAILQ_HEAD(ll_sched_qhead, ble_ll_sched_item) g_ble_ll_sched_q;
 
 #if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
 struct ble_ll_sched_obj g_ble_ll_sched_data;
 #endif
 
+static int
+preempt_any(struct ble_ll_sched_item *sch,
+            struct ble_ll_sched_item *item)
+{
+    return 1;
+}
+
+static int
+preempt_none(struct ble_ll_sched_item *sch,
+             struct ble_ll_sched_item *item)
+{
+    return 0;
+}
+
+static int
+preempt_any_except_conn(struct ble_ll_sched_item *sch,
+                        struct ble_ll_sched_item *item)
+{
+    BLE_LL_ASSERT(sch->sched_type == BLE_LL_SCHED_TYPE_CONN);
+
+    if (item->sched_type != BLE_LL_SCHED_TYPE_CONN) {
+        return 1;
+    }
+
+    return ble_ll_conn_is_lru(sch->cb_arg, item->cb_arg);
+}
+
+static inline int
+ble_ll_sched_check_overlap(struct ble_ll_sched_item *sch1,
+                           struct ble_ll_sched_item *sch2)
+{
+    /* Note: item ranges are defined as [start, end) so items do not overlap
+     *       if one item starts at the same time as another ends.
+     */
+    return CPUTIME_GT(sch1->end_time, sch2->start_time) &&
+           CPUTIME_GT(sch2->end_time, sch1->start_time);
+}
+
+static void
+ble_ll_sched_preempt(struct ble_ll_sched_item *sch,
+                     struct ble_ll_sched_item *first)
+{
+    struct ble_ll_sched_item *entry;
+    struct ble_ll_sched_item *next;
+    struct ble_ll_conn_sm *connsm;
+
+    entry = first;
+
+    do {
+        next = TAILQ_NEXT(entry, link);
+
+        TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link);
+        entry->enqueued = 0;
+
+        switch (entry->sched_type) {
+            case BLE_LL_SCHED_TYPE_CONN:
+                connsm = (struct ble_ll_conn_sm *)entry->cb_arg;
+                ble_ll_event_send(&connsm->conn_ev_end);
+                break;
+            case BLE_LL_SCHED_TYPE_ADV:
+                ble_ll_adv_event_rmvd_from_sched(entry->cb_arg);
+                break;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+            case BLE_LL_SCHED_TYPE_SCAN_AUX:
+                ble_ll_scan_aux_break(entry->cb_arg);
+                break;
+            case BLE_LL_SCHED_TYPE_AUX_SCAN:
+                ble_ll_scan_end_adv_evt(entry->cb_arg);
+                break;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+            case BLE_LL_SCHED_TYPE_PERIODIC:
+                ble_ll_adv_periodic_rmvd_from_sched(entry->cb_arg);
+                break;
+            case BLE_LL_SCHED_TYPE_SYNC:
+                ble_ll_sync_rmvd_from_sched(entry->cb_arg);
+                break;
+#endif
+#endif
+            default:
+                BLE_LL_ASSERT(0);
+                break;
+        }
+
+        entry = next;
+    } while (entry != sch);
+}
+
+static inline void
+ble_ll_sched_q_head_changed(void)
+{
+    struct ble_ll_sched_item *first;
+
+    g_ble_ll_sched_timer_running = 0;
+    os_cputime_timer_stop(&g_ble_ll_sched_timer);
+
+    first = TAILQ_FIRST(&g_ble_ll_sched_q);
+    ble_ll_rfmgmt_sched_changed(first);
+}
+
+static inline void
+ble_ll_sched_restart(void)
+{
+    struct ble_ll_sched_item *first;
+
+    first = TAILQ_FIRST(&g_ble_ll_sched_q);
+
+    if (!g_ble_ll_sched_timer_running && first) {
+        g_ble_ll_sched_timer_running = 1;
+        os_cputime_timer_start(&g_ble_ll_sched_timer, first->start_time);
+    }
+}
+
+static int
+ble_ll_sched_insert(struct ble_ll_sched_item *sch, uint32_t max_delay,
+                    ble_ll_sched_preempt_cb_t preempt_cb)
+{
+    struct ble_ll_sched_item *preempt_first;
+    struct ble_ll_sched_item *first;
+    struct ble_ll_sched_item *entry;
+    uint32_t max_start_time;
+    uint32_t duration;
+
+    OS_ASSERT_CRITICAL();
+
+    preempt_first = NULL;
+
+    max_start_time = sch->start_time + max_delay;
+    duration = sch->end_time - sch->start_time;
+
+    first = TAILQ_FIRST(&g_ble_ll_sched_q);
+    if (!first) {
+        TAILQ_INSERT_HEAD(&g_ble_ll_sched_q, sch, link);
+        sch->enqueued = 1;
+        goto done;
+    }
+
+    TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
+        if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
+            TAILQ_INSERT_BEFORE(entry, sch, link);
+            sch->enqueued = 1;
+            goto done;
+        }
+
+        /* If current item overlaps our item check if we can preempt. If we
+         * cannot preempt, move our item past current item and see if it's
+         * still within allowed range.
+         */
+
+        if (ble_ll_sched_check_overlap(sch, entry)) {
+            if (preempt_cb(sch, entry)) {
+                if (!preempt_first) {
+                    preempt_first = entry;
+                }
+            } else {
+                sch->start_time = entry->end_time;
+
+                if ((max_delay == 0) || CPUTIME_GEQ(sch->start_time,
+                                                    max_start_time)) {
+                    sch->enqueued = 0;
+                    goto done;
+                }
+
+                sch->end_time = sch->start_time + duration;
+                preempt_first = NULL;
+            }
+        }
+    }
+
+    if (!entry) {
+        TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
+        sch->enqueued = 1;
+    }
+
+done:
+    if (preempt_first) {
+        BLE_LL_ASSERT(sch->enqueued);
+        ble_ll_sched_preempt(sch, preempt_first);
+    }
+
+    /* Pause scheduler if inserted as 1st item, we do not want to miss this
+     * one. Caller should restart outside critical section.
+     */
+    if (TAILQ_FIRST(&g_ble_ll_sched_q) == sch) {
+        BLE_LL_ASSERT(sch->enqueued);
+        ble_ll_sched_q_head_changed();
+    }
+
+    return sch->enqueued ? 0 : -1;
+}
+
+#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
 /**
  * Checks if two events in the schedule will overlap in time. NOTE: consecutive
  * schedule items can end and start at the same time.
@@ -98,6 +289,7 @@ ble_ll_sched_is_overlap(struct ble_ll_sched_item *s1,
 
     return rc;
 }
+#endif
 
 /*
  * Determines if the schedule item overlaps the currently running schedule
@@ -119,26 +311,7 @@ ble_ll_sched_overlaps_current(struct ble_ll_sched_item *sch)
     return rc;
 }
 
-static int
-ble_ll_sched_conn_overlap(struct ble_ll_sched_item *entry)
-{
-    int rc;
-    struct ble_ll_conn_sm *connsm;
-
-    /* Should only be advertising or a connection here */
-    if (entry->sched_type == BLE_LL_SCHED_TYPE_CONN) {
-        connsm = (struct ble_ll_conn_sm *)entry->cb_arg;
-        entry->enqueued = 0;
-        TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link);
-        ble_ll_event_send(&connsm->conn_ev_end);
-        rc = 0;
-    } else {
-        rc = -1;
-    }
-
-    return rc;
-}
-
+#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
 static struct ble_ll_sched_item *
 ble_ll_sched_insert_if_empty(struct ble_ll_sched_item *sch)
 {
@@ -151,18 +324,15 @@ ble_ll_sched_insert_if_empty(struct ble_ll_sched_item *sch)
     }
     return entry;
 }
+#endif
 
 int
 ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm)
 {
-    int rc;
-    os_sr_t sr;
-    uint32_t usecs;
     struct ble_ll_sched_item *sch;
-    struct ble_ll_sched_item *start_overlap;
-    struct ble_ll_sched_item *end_overlap;
-    struct ble_ll_sched_item *entry;
-    struct ble_ll_conn_sm *tmp;
+    uint32_t usecs;
+    os_sr_t sr;
+    int rc;
 
     /* Get schedule element from connection */
     sch = &connsm->conn_sch;
@@ -183,7 +353,6 @@ ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm)
         return -1;
     }
 
-    /* We have to find a place for this schedule */
     OS_ENTER_CRITICAL(sr);
 
     if (ble_ll_sched_overlaps_current(sch)) {
@@ -191,101 +360,11 @@ ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm)
         return -1;
     }
 
-    /* Stop timer since we will add an element */
-    os_cputime_timer_stop(&g_ble_ll_sched_timer);
-
-    start_overlap = NULL;
-    end_overlap = NULL;
-    rc = 0;
-    TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-        if (ble_ll_sched_is_overlap(sch, entry)) {
-           if (entry->sched_type == BLE_LL_SCHED_TYPE_CONN &&
-                            !ble_ll_conn_is_lru((struct ble_ll_conn_sm *)sch->cb_arg,
-                                    (struct ble_ll_conn_sm *)entry->cb_arg)) {
-                /* Only insert if this element is older than all that we
-                 * overlap
-                 */
-                start_overlap = NULL;
-                rc = -1;
-                break;
-            }
-
-            if (start_overlap == NULL) {
-                start_overlap = entry;
-                end_overlap = entry;
-            } else {
-                end_overlap = entry;
-            }
-        } else {
-            if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                rc = 0;
-                TAILQ_INSERT_BEFORE(entry, sch, link);
-                break;
-            }
-        }
-    }
-
-    if (!rc) {
-        if (!entry) {
-            TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        }
-        sch->enqueued = 1;
-    }
-
-    /* Remove first to last scheduled elements */
-    entry = start_overlap;
-    while (entry) {
-        start_overlap = TAILQ_NEXT(entry,link);
-        switch (entry->sched_type) {
-        case BLE_LL_SCHED_TYPE_CONN:
-            tmp = (struct ble_ll_conn_sm *)entry->cb_arg;
-            ble_ll_event_send(&tmp->conn_ev_end);
-            break;
-        case BLE_LL_SCHED_TYPE_ADV:
-            ble_ll_adv_event_rmvd_from_sched((struct ble_ll_adv_sm *)entry->cb_arg);
-            break;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-        case BLE_LL_SCHED_TYPE_SCAN_AUX:
-            ble_ll_scan_aux_break(entry->cb_arg);
-            break;
-        case BLE_LL_SCHED_TYPE_AUX_SCAN:
-            ble_ll_scan_end_adv_evt((struct ble_ll_aux_data *)entry->cb_arg);
-            break;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
-        case BLE_LL_SCHED_TYPE_PERIODIC:
-            ble_ll_adv_periodic_rmvd_from_sched((struct ble_ll_adv_sm *)entry->cb_arg);
-            break;
-        case BLE_LL_SCHED_TYPE_SYNC:
-            ble_ll_sync_rmvd_from_sched((struct ble_ll_sync_sm *)entry->cb_arg);
-            break;
-#endif
-#endif
-        default:
-            BLE_LL_ASSERT(0);
-            break;
-        }
-
-        TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link);
-        entry->enqueued = 0;
-
-        if (entry == end_overlap) {
-            break;
-        }
-        entry = start_overlap;
-    }
-
-    entry = TAILQ_FIRST(&g_ble_ll_sched_q);
-    if (entry == sch) {
-        ble_ll_rfmgmt_sched_changed(sch);
-    } else {
-        sch = entry;
-    }
+    rc = ble_ll_sched_insert(sch, 0, preempt_any_except_conn);
 
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
@@ -405,7 +484,7 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm,
         }
     }
     earliest_start += MYNEWT_VAL(BLE_LL_CONN_INIT_MIN_WIN_OFFSET) *
-                      BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT;
+                      BLE_LL_SCHED_TICKS_PER_SLOT;
     itvl_t = connsm->conn_itvl_ticks;
 
     /* We have to find a place for this schedule */
@@ -540,27 +619,17 @@ int
 ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm,
                         struct ble_mbuf_hdr *ble_hdr, uint8_t pyld_len)
 {
-    int rc;
-    os_sr_t sr;
-    uint8_t req_slots;
-    uint32_t initial_start;
+    struct ble_ll_sched_item *sch;
+    uint32_t orig_start_time;
     uint32_t earliest_start;
-    uint32_t earliest_end;
-    uint32_t dur;
-    uint32_t itvl_t;
+    uint32_t min_win_offset;
+    uint32_t max_delay;
     uint32_t adv_rxend;
-    struct ble_ll_sched_item *entry;
-    struct ble_ll_sched_item *sch;
-
-    /*
-     * XXX: TODO this code assumes the advertisement and connect request were
-     * sent at 1Mbps.
-     */
+    os_sr_t sr;
+    int rc;
 
     /* Get schedule element from connection */
-    rc = -1;
     sch = &connsm->conn_sch;
-    req_slots = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS);
 
     /* XXX:
      * The calculations for the 32kHz crystal bear alot of explanation. The
@@ -603,7 +672,6 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm,
      * wont be listening. We could do better calculation if we wanted to use
      * a transmit window of 1 as opposed to 2, but for now we dont care.
      */
-    dur = req_slots * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT;
     adv_rxend = os_cputime_get32();
     if (ble_hdr->rxinfo.channel >= BLE_PHY_NUM_DATA_CHANS) {
         /*
@@ -635,86 +703,38 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm,
             BLE_LL_ASSERT(0);
         }
     }
-    earliest_start += MYNEWT_VAL(BLE_LL_CONN_INIT_MIN_WIN_OFFSET) *
-                      BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT;
-    earliest_end = earliest_start + dur;
-    itvl_t = connsm->conn_itvl_ticks;
 
-    /* We have to find a place for this schedule */
-    OS_ENTER_CRITICAL(sr);
+    sch->start_time = earliest_start - g_ble_ll_sched_offset_ticks;
+    sch->end_time = earliest_start + MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) *
+                                     BLE_LL_SCHED_TICKS_PER_SLOT;
 
-    /* The schedule item must occur after current running item (if any) */
-    sch->start_time = earliest_start;
-    initial_start = earliest_start;
+    orig_start_time = sch->start_time;
 
-    if (!ble_ll_sched_insert_if_empty(sch)) {
-        /* Nothing in schedule. Schedule as soon as possible */
-        rc = 0;
-        connsm->tx_win_off = MYNEWT_VAL(BLE_LL_CONN_INIT_MIN_WIN_OFFSET);
-    } else {
-        os_cputime_timer_stop(&g_ble_ll_sched_timer);
-        TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-            /* Set these because overlap function needs them to be set */
-            sch->start_time = earliest_start;
-            sch->end_time = earliest_end;
+    min_win_offset = MYNEWT_VAL(BLE_LL_CONN_INIT_MIN_WIN_OFFSET) *
+                     BLE_LL_SCHED_TICKS_PER_SLOT;
+    sch->start_time += min_win_offset;
+    sch->end_time += min_win_offset;
 
-            /* We can insert if before entry in list */
-            if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                if ((earliest_start - initial_start) <= itvl_t) {
-                    rc = 0;
-                    TAILQ_INSERT_BEFORE(entry, sch, link);
-                }
-                break;
-            }
+    max_delay = connsm->conn_itvl_ticks - min_win_offset;
 
-            /* Check for overlapping events */
-            if (ble_ll_sched_is_overlap(sch, entry)) {
-                /* Earliest start is end of this event since we overlap */
-                earliest_start = entry->end_time;
-                earliest_end = earliest_start + dur;
-            }
-        }
+    OS_ENTER_CRITICAL(sr);
 
-        /* Must be able to schedule within one connection interval */
-        if (!entry) {
-            if ((earliest_start - initial_start) <= itvl_t) {
-                rc = 0;
-                TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-            }
-        }
+    rc = ble_ll_sched_insert(sch, max_delay, preempt_none);
+
+    if (rc == 0) {
+        connsm->tx_win_off = os_cputime_ticks_to_usecs(sch->start_time -
+                                                       orig_start_time) /
+                             BLE_LL_CONN_TX_OFF_USECS;
+
+        connsm->anchor_point = sch->start_time + g_ble_ll_sched_offset_ticks;
+        connsm->anchor_point_usecs = 0;
+        connsm->ce_end_time = sch->end_time;
 
-        if (!rc) {
-            /* calculate number of window offsets. Each offset is 1.25 ms */
-            sch->enqueued = 1;
-            /*
-             * NOTE: we dont add sched offset ticks as we want to under-estimate
-             * the transmit window slightly since the window size is currently
-             * 2 when using a 32768 crystal.
-             */
-            dur = os_cputime_ticks_to_usecs(earliest_start - initial_start);
-            connsm->tx_win_off = dur / BLE_LL_CONN_TX_OFF_USECS;
-        }
     }
 
-    if (!rc) {
-        sch->start_time = earliest_start;
-        sch->end_time = earliest_end;
-        /*
-         * Since we have the transmit window to transmit in, we dont need
-         * to set the anchor point usecs; just transmit to the nearest tick.
-         */
-        connsm->anchor_point = earliest_start + g_ble_ll_sched_offset_ticks;
-        connsm->anchor_point_usecs = 0;
-        connsm->ce_end_time = earliest_end;
-    }
-
-    /* Get head of list to restart timer */
-    sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-    ble_ll_rfmgmt_sched_changed(sch);
-
     OS_EXIT_CRITICAL(sr);
 
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
@@ -732,15 +752,11 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm,
 int
 ble_ll_sched_slave_new(struct ble_ll_conn_sm *connsm)
 {
-    int rc;
-    os_sr_t sr;
-    struct ble_ll_sched_item *entry;
-    struct ble_ll_sched_item *next_sch;
     struct ble_ll_sched_item *sch;
-    int first = 0;
+    os_sr_t sr;
+    int rc;
 
     /* Get schedule element from connection */
-    rc = -1;
     sch = &connsm->conn_sch;
 
     /* Set schedule start and end times */
@@ -755,68 +771,13 @@ ble_ll_sched_slave_new(struct ble_ll_conn_sm *connsm)
     sch->end_time = connsm->ce_end_time;
     sch->remainder = 0;
 
-    /* We have to find a place for this schedule */
     OS_ENTER_CRITICAL(sr);
 
-    /* The schedule item must occur after current running item (if any) */
-    if (ble_ll_sched_overlaps_current(sch)) {
-        OS_EXIT_CRITICAL(sr);
-        return rc;
-    }
-
-    entry = ble_ll_sched_insert_if_empty(sch);
-    if (!entry) {
-        /* Nothing in schedule. Schedule as soon as possible */
-        rc = 0;
-        first = 1;
-    } else {
-        os_cputime_timer_stop(&g_ble_ll_sched_timer);
-        while (1) {
-            next_sch = entry->link.tqe_next;
-            /* Insert if event ends before next starts */
-            if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                rc = 0;
-                TAILQ_INSERT_BEFORE(entry, sch, link);
-                break;
-            }
-
-            if (ble_ll_sched_is_overlap(sch, entry)) {
-                /* If we overlap with a connection, we re-schedule */
-                if (ble_ll_sched_conn_overlap(entry)) {
-                    break;
-                }
-            }
-
-            /* Move to next entry */
-            entry = next_sch;
-
-            /* Insert at tail if none left to check */
-            if (!entry) {
-                rc = 0;
-                TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-                break;
-            }
-        }
-
-        if (!rc) {
-            sch->enqueued = 1;
-        }
-
-        next_sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-        if (next_sch == sch) {
-            first = 1;
-        } else {
-            sch = next_sch;
-        }
-    }
-
-    if (first) {
-        ble_ll_rfmgmt_sched_changed(sch);
-    }
+    rc = ble_ll_sched_insert(sch, 0, preempt_any);
 
     OS_EXIT_CRITICAL(sr);
 
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
@@ -853,7 +814,6 @@ ble_ll_sched_sync_reschedule(struct ble_ll_sched_item *sch,
                              uint32_t window_widening,
                              int8_t phy_mode)
 {
-    struct ble_ll_sched_item *entry;
     uint8_t start_time_rem_usecs;
     uint8_t window_rem_usecs;
     uint32_t window_ticks;
@@ -893,7 +853,6 @@ ble_ll_sched_sync_reschedule(struct ble_ll_sched_item *sch,
         return -1;
     }
 
-    /* We have to find a place for this schedule */
     OS_ENTER_CRITICAL(sr);
 
     if (ble_ll_sched_sync_overlaps_current(sch)) {
@@ -901,43 +860,11 @@ ble_ll_sched_sync_reschedule(struct ble_ll_sched_item *sch,
         return -1;
     }
 
-    /* Try to find slot for sync scan. */
-    os_cputime_timer_stop(&g_ble_ll_sched_timer);
-
-    TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-        /* We can insert if before entry in list */
-        if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-            TAILQ_INSERT_BEFORE(entry, sch, link);
-            sch->enqueued = 1;
-            break;
-        }
-
-        /* Check for overlapping events. For now drop if it overlaps with
-         * anything. We can make it smarter later on
-         */
-        if (ble_ll_sched_is_overlap(sch, entry)) {
-            rc = -1;
-            break;
-        }
-    }
-
-    if (!entry) {
-        TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        sch->enqueued = 1;
-    }
-
-    entry = TAILQ_FIRST(&g_ble_ll_sched_q);
-    if (entry == sch) {
-        ble_ll_rfmgmt_sched_changed(sch);
-    } else {
-        sch = entry;
-    }
+    rc = ble_ll_sched_insert(sch, 0, preempt_none);
 
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
@@ -947,7 +874,6 @@ ble_ll_sched_sync(struct ble_ll_sched_item *sch,
                   uint32_t beg_cputime, uint32_t rem_usecs,
                   uint32_t offset, int8_t phy_mode)
 {
-    struct ble_ll_sched_item *entry;
     uint32_t start_time_rem_usecs;
     uint32_t off_rem_usecs;
     uint32_t start_time;
@@ -979,51 +905,14 @@ ble_ll_sched_sync(struct ble_ll_sched_item *sch,
 
     OS_ENTER_CRITICAL(sr);
 
-    if (!ble_ll_sched_insert_if_empty(sch)) {
-        /* Nothing in schedule. Schedule as soon as possible
-         * If we are here it means sch has been added to the scheduler */
-        goto done;
-    }
-
-    /* Try to find slot for scan. */
-    os_cputime_timer_stop(&g_ble_ll_sched_timer);
-    TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-        /* We can insert if before entry in list */
-        if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-            TAILQ_INSERT_BEFORE(entry, sch, link);
-            sch->enqueued = 1;
-            break;
-        }
-
-        /* Check for overlapping events. For now drop if it overlaps with
-         * anything. We can make it smarter later on
-         */
-        if (ble_ll_sched_is_overlap(sch, entry)) {
-            rc = -1;
-            break;
-        }
-    }
-
-    if (!entry) {
-        TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        sch->enqueued = 1;
-    }
-
-done:
-    entry = TAILQ_FIRST(&g_ble_ll_sched_q);
-    if (entry == sch) {
-        ble_ll_rfmgmt_sched_changed(sch);
-    } else {
-        sch = entry;
-    }
+    rc = ble_ll_sched_insert(sch, 0, preempt_none);
 
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     STATS_INC(ble_ll_stats, sync_scheduled);
+
     return rc;
 }
 #endif
@@ -1033,257 +922,87 @@ ble_ll_sched_adv_new(struct ble_ll_sched_item *sch, ble_ll_sched_adv_new_cb cb,
                      void *arg)
 {
     os_sr_t sr;
-    uint32_t adv_start;
-    uint32_t duration;
-    struct ble_ll_sched_item *entry;
-    struct ble_ll_sched_item *orig;
-
-    /* Get length of schedule item */
-    duration = sch->end_time - sch->start_time;
-    orig = sch;
+    int rc;
 
     OS_ENTER_CRITICAL(sr);
-    entry = ble_ll_sched_insert_if_empty(sch);
-    if (!entry) {
-        adv_start = sch->start_time;
-    } else {
-        /* XXX: no need to stop timer if not first on list. Modify code? */
-        os_cputime_timer_stop(&g_ble_ll_sched_timer);
-        TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-            /* We can insert if before entry in list */
-            if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                TAILQ_INSERT_BEFORE(entry, sch, link);
-                break;
-            }
-
-            /* Check for overlapping events */
-            if (ble_ll_sched_is_overlap(sch, entry)) {
-                /* Earliest start is end of this event since we overlap */
-                sch->start_time = entry->end_time;
-                sch->end_time = sch->start_time + duration;
-            }
-        }
 
-        if (!entry) {
-            TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        }
-        adv_start = sch->start_time;
+    rc = ble_ll_sched_insert(sch, BLE_LL_SCHED_MAX_DELAY_ANY,
+                             preempt_none);
+    BLE_LL_ASSERT(rc == 0);
 
-        sch->enqueued = 1;
-
-        /* Restart with head of list */
-        sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-    }
-
-    if (cb) {
-        cb((struct ble_ll_adv_sm *)orig->cb_arg, adv_start, arg);
-    }
-
-    if (orig == sch) {
-        ble_ll_rfmgmt_sched_changed(sch);
-    }
+    cb(sch->cb_arg, sch->start_time, arg);
 
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
-    return 0;
+    return rc;
 }
 
 int
-ble_ll_sched_periodic_adv(struct ble_ll_sched_item *sch, uint32_t *start,
-                          bool after_overlap)
+ble_ll_sched_periodic_adv(struct ble_ll_sched_item *sch, bool first_event)
 {
-    int rc = 0;
     os_sr_t sr;
-    uint32_t adv_start;
-    uint32_t duration;
-    struct ble_ll_sched_item *entry;
-    struct ble_ll_sched_item *orig = sch;
-
-    /* Get length of schedule item */
-    duration = sch->end_time - sch->start_time;
+    int rc;
 
     OS_ENTER_CRITICAL(sr);
-    entry = ble_ll_sched_insert_if_empty(sch);
-    if (!entry) {
-        adv_start = sch->start_time;
-    } else {
-        /* XXX: no need to stop timer if not first on list. Modify code? */
-        os_cputime_timer_stop(&g_ble_ll_sched_timer);
-        TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-            /* We can insert if before entry in list */
-            if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                TAILQ_INSERT_BEFORE(entry, sch, link);
-                break;
-            }
 
-            /* Check for overlapping events */
-            if (ble_ll_sched_is_overlap(sch, entry)) {
-                if (after_overlap) {
-                    /* Earliest start is end of this event since we overlap */
-                    sch->start_time = entry->end_time;
-                    sch->end_time = sch->start_time + duration;
-                } else {
-                    rc = -1;
-                    break;
-                }
-            }
-        }
-
-        if (!entry) {
-            TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        }
-        adv_start = sch->start_time;
-
-        if (!rc) {
-            sch->enqueued = 1;
-        }
-
-        /* Restart with head of list */
-        sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-    }
-
-    if (!rc) {
-        *start = adv_start;
-    }
-
-    if (orig == sch) {
-        ble_ll_rfmgmt_sched_changed(sch);
+    if (first_event) {
+        rc = ble_ll_sched_insert(sch, BLE_LL_SCHED_MAX_DELAY_ANY,
+                                 preempt_none);
+    } else {
+        rc = ble_ll_sched_insert(sch, 0, preempt_any);
     }
 
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
 
 int
-ble_ll_sched_adv_reschedule(struct ble_ll_sched_item *sch, uint32_t *start,
+ble_ll_sched_adv_reschedule(struct ble_ll_sched_item *sch,
                             uint32_t max_delay_ticks)
 {
-    int rc;
-    os_sr_t sr;
-    uint32_t orig_start;
-    uint32_t duration;
+    struct ble_ll_sched_item *next;
+    uint32_t max_end_time;
     uint32_t rand_ticks;
-    struct ble_ll_sched_item *entry;
-    struct ble_ll_sched_item *next_sch;
-    struct ble_ll_sched_item *before;
-    struct ble_ll_sched_item *start_overlap;
-    struct ble_ll_sched_item *end_overlap;
-
-    /* Get length of schedule item */
-    duration = sch->end_time - sch->start_time;
+    os_sr_t sr;
+    int rc;
 
-    /* Add maximum randomization delay to end */
-    rand_ticks = max_delay_ticks;
-    sch->end_time += max_delay_ticks;
+    max_end_time = sch->end_time + max_delay_ticks;
 
-    start_overlap = NULL;
-    end_overlap = NULL;
-    before = NULL;
-    rc = 0;
     OS_ENTER_CRITICAL(sr);
 
-    entry = ble_ll_sched_insert_if_empty(sch);
-    if (entry) {
-        os_cputime_timer_stop(&g_ble_ll_sched_timer);
-        while (1) {
-            next_sch = entry->link.tqe_next;
-            if (ble_ll_sched_is_overlap(sch, entry)) {
-                if (start_overlap == NULL) {
-                    start_overlap = entry;
-                    end_overlap = entry;
-                } else {
-                    end_overlap = entry;
-                }
-            } else {
-                if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                    before = entry;
-                    break;
-                }
-            }
-
-            entry = next_sch;
-            if (entry == NULL) {
-                break;
-            }
-        }
+    /* Try to schedule as early as possible but no later than max allowed delay.
+     * If succeeded, randomize start time to be within max allowed delay from
+     * the original start time but make sure it ends before next scheduled item.
+     */
 
-        /*
-         * If there is no overlap, we either insert before the 'before' entry
-         * or we insert at the end if there is no before entry.
-         */
-        if (start_overlap == NULL) {
-            if (before) {
-                TAILQ_INSERT_BEFORE(before, sch, link);
-            } else {
-                TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
+    rc = ble_ll_sched_insert(sch, max_delay_ticks, preempt_none);
+    if (rc == 0) {
+        next = TAILQ_NEXT(sch, link);
+        if (next) {
+            if (CPUTIME_LT(next->start_time, max_end_time)) {
+                max_end_time = next->start_time;
             }
+            rand_ticks = max_end_time - sch->end_time;
         } else {
-            /*
-             * This item will overlap with others. See if we can fit it in
-             * with original duration.
-             */
-            before = NULL;
-            orig_start = sch->start_time;
-            entry = start_overlap;
-            sch->end_time = sch->start_time + duration;
-            while (1) {
-                next_sch = entry->link.tqe_next;
-                if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                    rand_ticks = entry->start_time - sch->end_time;
-                    before = entry;
-                    TAILQ_INSERT_BEFORE(before, sch, link);
-                    break;
-                } else {
-                    sch->start_time = entry->end_time;
-                    sch->end_time = sch->start_time + duration;
-                }
-
-                if (entry == end_overlap) {
-                    rand_ticks = (orig_start + max_delay_ticks) - sch->start_time;
-                    if (rand_ticks > max_delay_ticks) {
-                        /* No place for advertisement. */
-                        rc = -1;
-                    } else {
-                        if (next_sch == NULL) {
-                            TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-                        } else {
-                            TAILQ_INSERT_BEFORE(next_sch, sch, link);
-                        }
-                    }
-                    break;
-                }
-                entry = next_sch;
-                BLE_LL_ASSERT(entry != NULL);
-            }
+            rand_ticks = max_delay_ticks;
         }
-    }
 
-    if (!rc) {
-        sch->enqueued = 1;
         if (rand_ticks) {
-            sch->start_time += ble_ll_rand() % rand_ticks;
+            rand_ticks = ble_ll_rand() % rand_ticks;
         }
-        sch->end_time = sch->start_time + duration;
-        *start = sch->start_time;
 
-        if (sch == TAILQ_FIRST(&g_ble_ll_sched_q)) {
-            ble_ll_rfmgmt_sched_changed(sch);
-        }
+        sch->start_time += rand_ticks;
+        sch->end_time += rand_ticks;
     }
 
     OS_EXIT_CRITICAL(sr);
 
-    sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
@@ -1293,36 +1012,24 @@ ble_ll_sched_adv_resched_pdu(struct ble_ll_sched_item *sch)
 {
     uint8_t lls;
     os_sr_t sr;
-    struct ble_ll_sched_item *entry;
+    int rc;
 
     OS_ENTER_CRITICAL(sr);
 
     lls = ble_ll_state_get();
     if ((lls == BLE_LL_STATE_ADV) || (lls == BLE_LL_STATE_CONNECTION) ||
-            (lls == BLE_LL_STATE_SYNC)) {
-        goto adv_resched_pdu_fail;
-    }
-
-    entry = ble_ll_sched_insert_if_empty(sch);
-    if (entry) {
-        /* If we overlap with the first item, simply re-schedule */
-        if (ble_ll_sched_is_overlap(sch, entry)) {
-            goto adv_resched_pdu_fail;
-        }
-        os_cputime_timer_stop(&g_ble_ll_sched_timer);
-        TAILQ_INSERT_BEFORE(entry, sch, link);
-        sch->enqueued = 1;
+        (lls == BLE_LL_STATE_SYNC)) {
+        OS_EXIT_CRITICAL(sr);
+        return -1;
     }
 
-    ble_ll_rfmgmt_sched_changed(TAILQ_FIRST(&g_ble_ll_sched_q));
+    rc = ble_ll_sched_insert(sch, 0, preempt_none);
 
     OS_EXIT_CRITICAL(sr);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
-    return 0;
 
-adv_resched_pdu_fail:
-    OS_EXIT_CRITICAL(sr);
-    return -1;
+    ble_ll_sched_restart();
+
+    return rc;
 }
 
 /**
@@ -1335,75 +1042,71 @@ adv_resched_pdu_fail:
 int
 ble_ll_sched_rmv_elem(struct ble_ll_sched_item *sch)
 {
+    uint8_t first_removed;
     os_sr_t sr;
-    struct ble_ll_sched_item *first;
-    int rc = 1;
+    int rc;
 
-    if (!sch) {
-        return rc;
-    }
+    BLE_LL_ASSERT(sch);
 
     OS_ENTER_CRITICAL(sr);
+
+    first_removed = 0;
+
     if (sch->enqueued) {
-        first = TAILQ_FIRST(&g_ble_ll_sched_q);
-        if (first == sch) {
-            os_cputime_timer_stop(&g_ble_ll_sched_timer);
+        if (sch == TAILQ_FIRST(&g_ble_ll_sched_q)) {
+            first_removed = 1;
         }
 
         TAILQ_REMOVE(&g_ble_ll_sched_q, sch, link);
         sch->enqueued = 0;
+
         rc = 0;
+    } else {
+        rc = 1;
+    }
 
-        if (first == sch) {
-            first = TAILQ_FIRST(&g_ble_ll_sched_q);
-            if (first) {
-                os_cputime_timer_start(&g_ble_ll_sched_timer, first->start_time);
-            }
-            ble_ll_rfmgmt_sched_changed(first);
-        }
+    if (first_removed) {
+        ble_ll_sched_q_head_changed();
     }
+
     OS_EXIT_CRITICAL(sr);
 
+    ble_ll_sched_restart();
+
     return rc;
 }
 
 void
 ble_ll_sched_rmv_elem_type(uint8_t type, sched_remove_cb_func remove_cb)
 {
-    os_sr_t sr;
-    struct ble_ll_sched_item *entry;
     struct ble_ll_sched_item *first;
+    struct ble_ll_sched_item *entry;
+    uint8_t first_removed;
+    os_sr_t sr;
 
     OS_ENTER_CRITICAL(sr);
-    first = TAILQ_FIRST(&g_ble_ll_sched_q);
 
-    if (!first) {
-        OS_EXIT_CRITICAL(sr);
-        return;
+    first = TAILQ_FIRST(&g_ble_ll_sched_q);
+    if (first->sched_type == type) {
+        first_removed = 1;
     }
 
     TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-        if (entry->sched_type == type) {
-            if (first == entry) {
-                os_cputime_timer_stop(&g_ble_ll_sched_timer);
-                first = NULL;
-            }
-
-            TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link);
-            remove_cb(entry);
-            entry->enqueued = 0;
+        if (entry->sched_type != type) {
+            continue;
         }
+        TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link);
+        remove_cb(entry);
+        entry->enqueued = 0;
     }
 
-    if (!first) {
-        first = TAILQ_FIRST(&g_ble_ll_sched_q);
-        if (first) {
-            os_cputime_timer_start(&g_ble_ll_sched_timer, first->start_time);
-        }
-        ble_ll_rfmgmt_sched_changed(first);
+    if (first_removed) {
+        ble_ll_sched_q_head_changed();
     }
 
     OS_EXIT_CRITICAL(sr);
+
+    ble_ll_sched_restart();
 }
 
 /**
@@ -1481,6 +1184,7 @@ ble_ll_sched_execute_item(struct ble_ll_sched_item *sch)
 sched:
     BLE_LL_DEBUG_GPIO(SCHED_ITEM_CB, 1);
     BLE_LL_ASSERT(sch->sched_cb);
+
     rc = sch->sched_cb(sch);
     BLE_LL_DEBUG_GPIO(SCHED_ITEM_CB, 0);
     return rc;
@@ -1500,6 +1204,8 @@ ble_ll_sched_run(void *arg)
 
     BLE_LL_DEBUG_GPIO(SCHED_RUN, 1);
 
+    g_ble_ll_sched_timer_running = 0;
+
     /* Look through schedule queue */
     sch = TAILQ_FIRST(&g_ble_ll_sched_q);
     if (sch) {
@@ -1520,13 +1226,7 @@ ble_ll_sched_run(void *arg)
         TAILQ_REMOVE(&g_ble_ll_sched_q, sch, link);
         sch->enqueued = 0;
         ble_ll_sched_execute_item(sch);
-
-        /* Restart if there is an item on the schedule */
-        sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-        if (sch) {
-            os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
-        }
-        ble_ll_rfmgmt_sched_changed(sch);
+        ble_ll_sched_restart();
     }
 
     BLE_LL_DEBUG_GPIO(SCHED_RUN, 0);
@@ -1586,7 +1286,6 @@ ble_ll_sched_aux_scan(struct ble_mbuf_hdr *ble_hdr,
     uint32_t start_time_rem_usecs;
     uint32_t end_time;
     uint32_t dur;
-    struct ble_ll_sched_item *entry;
     struct ble_ll_sched_item *sch;
     int phy_mode;
 
@@ -1621,58 +1320,16 @@ ble_ll_sched_aux_scan(struct ble_mbuf_hdr *ble_hdr,
 
     OS_ENTER_CRITICAL(sr);
 
-    if (!ble_ll_sched_insert_if_empty(sch)) {
-        /* Nothing in schedule. Schedule as soon as possible
-         * If we are here it means sch has been added to the scheduler */
-        rc = 0;
-        goto done;
-    }
-
-    /* Try to find slot for aux scan. */
-    os_cputime_timer_stop(&g_ble_ll_sched_timer);
-    TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-        /* We can insert if before entry in list */
-        if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-            rc = 0;
-            TAILQ_INSERT_BEFORE(entry, sch, link);
-            sch->enqueued = 1;
-            break;
-        }
-
-        /* Check for overlapping events. For now drop if it overlaps with
-         * anything. We can make it smarter later on
-         */
-        if (ble_ll_sched_is_overlap(sch, entry)) {
-            break;
-        }
-    }
-
-    if (!entry) {
-        rc = 0;
-        TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        sch->enqueued = 1;
-    }
-
-done:
+    rc = ble_ll_sched_insert(sch, 0, preempt_none);
 
     if (rc == 0) {
         sch->cb_arg = ble_ll_scan_aux_data_ref(aux_scan);
         STATS_INC(ble_ll_stats, aux_scheduled);
     }
 
-    /* Get head of list to restart timer */
-    entry = TAILQ_FIRST(&g_ble_ll_sched_q);
-    if (entry == sch) {
-        ble_ll_rfmgmt_sched_changed(sch);
-    } else {
-        sch = entry;
-    }
-
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
@@ -1681,7 +1338,6 @@ int
 ble_ll_sched_scan_aux(struct ble_ll_sched_item *sch, uint32_t pdu_time,
                       uint8_t pdu_time_rem, uint32_t offset_us)
 {
-    struct ble_ll_sched_item *entry;
     uint32_t offset_ticks;
     os_sr_t sr;
     int rc;
@@ -1696,42 +1352,11 @@ ble_ll_sched_scan_aux(struct ble_ll_sched_item *sch, uint32_t pdu_time,
 
     OS_ENTER_CRITICAL(sr);
 
-    if (!ble_ll_sched_insert_if_empty(sch)) {
-        /* Scheduler was empty, inserted as 1st element */
-        rc = 0;
-        goto done;
-    }
-
-    os_cputime_timer_stop(&g_ble_ll_sched_timer);
-    TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-        if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-            TAILQ_INSERT_BEFORE(entry, sch, link);
-            sch->enqueued = 1;
-            rc = 0;
-            break;
-        }
-
-        if (ble_ll_sched_is_overlap(sch, entry)) {
-            rc = -1;
-            break;
-        }
-    }
-
-    if (!entry) {
-        TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        sch->enqueued = 1;
-        rc = 0;
-    }
-
-done:
-    entry = TAILQ_FIRST(&g_ble_ll_sched_q);
+    rc = ble_ll_sched_insert(sch, 0, preempt_none);
 
     OS_EXIT_CRITICAL(sr);
 
-    if (entry == sch) {
-        ble_ll_rfmgmt_sched_changed(sch);
-    }
-    os_cputime_timer_start(&g_ble_ll_sched_timer, entry->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
@@ -1740,57 +1365,18 @@ done:
 #if MYNEWT_VAL(BLE_LL_DTM)
 int ble_ll_sched_dtm(struct ble_ll_sched_item *sch)
 {
-    int rc;
     os_sr_t sr;
-    struct ble_ll_sched_item *entry;
+    int rc;
 
     OS_ENTER_CRITICAL(sr);
 
-    if (!ble_ll_sched_insert_if_empty(sch)) {
-        /* Nothing in schedule. Schedule as soon as possible
-         * If we are here it means sch has been added to the scheduler */
-        rc = 0;
-        goto done;
-    }
-
-    /* Try to find slot for test. */
-    os_cputime_timer_stop(&g_ble_ll_sched_timer);
-    TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-        /* We can insert if before entry in list */
-        if (sch->end_time <= entry->start_time) {
-            rc = 0;
-            TAILQ_INSERT_BEFORE(entry, sch, link);
-            sch->enqueued = 1;
-            break;
-        }
-
-        /* Check for overlapping events. For now drop if it overlaps with
-         * anything. We can make it smarter later on
-         */
-        if (ble_ll_sched_is_overlap(sch, entry)) {
-            OS_EXIT_CRITICAL(sr);
-            return -1;
-        }
-    }
-
-    if (!entry) {
-        rc = 0;
-        TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        sch->enqueued = 1;
-    }
-
-done:
-
-    /* Get head of list to restart timer */
-    sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-
-    ble_ll_rfmgmt_sched_changed(sch);
+    rc = ble_ll_sched_insert(sch, 0, preempt_any);
 
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    if (rc == 0) {
+        ble_ll_sched_restart();
+    }
 
     return rc;
 }
@@ -1803,6 +1389,7 @@ done:
 void
 ble_ll_sched_stop(void)
 {
+    g_ble_ll_sched_timer_running = 0;
     os_cputime_timer_stop(&g_ble_ll_sched_timer);
 }