You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by we...@apache.org on 2017/03/21 20:59:44 UTC

[1/2] incubator-mynewt-core git commit: Initial commit for the "body sensor network" code changes.

Repository: incubator-mynewt-core
Updated Branches:
  refs/heads/bsnbranch [created] 631cc3d93


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/net/nimble/controller/src/ble_ll_sched.c
----------------------------------------------------------------------
diff --git a/net/nimble/controller/src/ble_ll_sched.c b/net/nimble/controller/src/ble_ll_sched.c
index dd42c3f..b31759b 100644
--- a/net/nimble/controller/src/ble_ll_sched.c
+++ b/net/nimble/controller/src/ble_ll_sched.c
@@ -56,6 +56,10 @@ int32_t g_ble_ll_sched_max_late;
 /* Queue for timers */
 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
+
 /**
  * Checks if two events in the schedule will overlap in time. NOTE: consecutive
  * schedule items can end and start at the same time.
@@ -247,6 +251,165 @@ ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm)
     return rc;
 }
 
+#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
+int
+ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, uint32_t adv_rxend,
+                        uint8_t req_slots)
+{
+    int rc;
+    os_sr_t sr;
+    uint32_t initial_start;
+    uint32_t earliest_start;
+    uint32_t earliest_end;
+    uint32_t dur;
+    uint32_t itvl_t;
+
+    int i;
+    uint32_t tpp;
+    uint32_t tse;
+    uint32_t np;
+    uint32_t cp;
+    uint32_t tick_in_period;
+
+    struct ble_ll_sched_item *entry;
+    struct ble_ll_sched_item *sch;
+
+    /* Better have a connsm */
+    assert(connsm != NULL);
+
+    /* Get schedule element from connection */
+    rc = -1;
+    sch = &connsm->conn_sch;
+
+    /*
+     * The earliest start time is 1.25 msecs from the end of the connect
+     * request transmission. Note that adv_rxend is the end of the received
+     * advertisement, so we need to add an IFS plus the time it takes to send
+     * the connection request
+     */
+    dur = os_cputime_usecs_to_ticks(g_ble_ll_sched_data.sch_ticks_per_period);
+    earliest_start = adv_rxend +
+        os_cputime_usecs_to_ticks(BLE_LL_IFS + BLE_LL_CONN_REQ_DURATION +
+                                  BLE_LL_CONN_INITIAL_OFFSET);
+    earliest_end = earliest_start + dur;
+    initial_start = earliest_start;
+
+    itvl_t = os_cputime_usecs_to_ticks(connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS);
+
+    /* We have to find a place for this schedule */
+    OS_ENTER_CRITICAL(sr);
+
+    /*
+     * Are there any allocated periods? If not, set epoch start to earliest
+     * time
+     */
+    if (g_ble_ll_sched_data.sch_num_occ_periods == 0) {
+        g_ble_ll_sched_data.sch_epoch_start = earliest_start;
+        cp = 0;
+    } else {
+        /*
+         * Earliest start must occur on period boundary.
+         * (tse = ticks since epoch)
+         */
+        tpp = g_ble_ll_sched_data.sch_ticks_per_period;
+        tse = earliest_start - g_ble_ll_sched_data.sch_epoch_start;
+        np = tse / tpp;
+        cp = np % BLE_LL_SCHED_PERIODS;
+        tick_in_period = tse - (np * tpp);
+        if (tick_in_period != 0) {
+            ++cp;
+            if (cp == BLE_LL_SCHED_PERIODS) {
+                cp = 0;
+            }
+            earliest_start += (tpp - tick_in_period);
+        }
+
+        /* Now find first un-occupied period starting from cp */
+        for (i = 0; i < BLE_LL_SCHED_PERIODS; ++i) {
+            if (g_ble_ll_sched_data.sch_occ_period_mask & (1 << cp)) {
+                ++cp;
+                if (cp == BLE_LL_SCHED_PERIODS) {
+                    cp = 0;
+                }
+                earliest_start += tpp;
+            } else {
+                /* not occupied */
+                break;
+            }
+        }
+        /* Should never happen but if it does... */
+        if (i == BLE_LL_SCHED_PERIODS) {
+            OS_EXIT_CRITICAL(sr);
+            return rc;
+        }
+    }
+
+    sch->start_time = earliest_start;
+    earliest_end = earliest_start + dur;
+
+    if (!ble_ll_sched_insert_if_empty(sch)) {
+        /* Nothing in schedule. Schedule as soon as possible */
+        rc = 0;
+        connsm->tx_win_off = 0;
+    } 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;
+
+            /* We can insert if before entry in list */
+            if ((int32_t)(sch->end_time - entry->start_time) <= 0) {
+                if ((earliest_start - initial_start) <= itvl_t) {
+                    rc = 0;
+                    TAILQ_INSERT_BEFORE(entry, sch, link);
+                }
+                break;
+            }
+
+            /* Check for overlapping events. Should not occur */
+            if (ble_ll_sched_is_overlap(sch, entry)) {
+                OS_EXIT_CRITICAL(sr);
+                return rc;
+            }
+        }
+
+        if (!entry) {
+            if ((earliest_start - initial_start) <= itvl_t) {
+                rc = 0;
+                TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
+            }
+        }
+
+        if (!rc) {
+            /* calculate number of connection intervals before start */
+            sch->enqueued = 1;
+            connsm->tx_win_off = (earliest_start - initial_start) /
+                os_cputime_usecs_to_ticks(BLE_LL_CONN_ITVL_USECS);
+        }
+    }
+
+    if (!rc) {
+        sch->start_time = earliest_start;
+        sch->end_time = earliest_end;
+        connsm->anchor_point = earliest_start +
+            os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS);
+        connsm->ce_end_time = earliest_end;
+        connsm->period_occ_mask = (1 << cp);
+        g_ble_ll_sched_data.sch_occ_period_mask |= connsm->period_occ_mask;
+        ++g_ble_ll_sched_data.sch_num_occ_periods;
+    }
+
+    /* Get head of list to restart timer */
+    sch = TAILQ_FIRST(&g_ble_ll_sched_q);
+
+    OS_EXIT_CRITICAL(sr);
+
+    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+
+    return rc;
+}
+#else
 int
 ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, uint32_t adv_rxend,
                         uint8_t req_slots)
@@ -370,6 +533,7 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, uint32_t adv_rxend,
 
     return rc;
 }
+#endif
 
 int
 ble_ll_sched_slave_new(struct ble_ll_conn_sm *connsm)
@@ -859,5 +1023,14 @@ ble_ll_sched_init(void)
 {
     /* Initialize cputimer for the scheduler */
     os_cputime_timer_init(&g_ble_ll_sched_timer, ble_ll_sched_run, NULL);
+
+#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
+    memset(&g_ble_ll_sched_data, 0, sizeof(struct ble_ll_sched_obj));
+    g_ble_ll_sched_data.sch_ticks_per_period =
+        os_cputime_usecs_to_ticks(MYNEWT_VAL(BLE_LL_USECS_PER_PERIOD));
+    g_ble_ll_sched_data.sch_ticks_per_epoch = BLE_LL_SCHED_PERIODS *
+        g_ble_ll_sched_data.sch_ticks_per_period;
+#endif
+
     return 0;
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/net/nimble/controller/syscfg.yml
----------------------------------------------------------------------
diff --git a/net/nimble/controller/syscfg.yml b/net/nimble/controller/syscfg.yml
index 357decc..39c3b44 100644
--- a/net/nimble/controller/syscfg.yml
+++ b/net/nimble/controller/syscfg.yml
@@ -128,6 +128,27 @@ syscfg.defs:
             scheduled items will be at least this far apart
         value: '4'
 
+    # Strict scheduling
+    BLE_LL_STRICT_CONN_SCHEDULING:
+        description: >
+            Forces the scheduler on a central to schedule connections in fixed
+            time intervals called periods. If set to 0, the scheduler is not forced
+            to do this. If set to 1, the scheduler will only schedule connections at
+            period boundaries. See comments in ble_ll_sched.h for more details.
+        value: '1'
+
+    BLE_LL_ADD_STRICT_SCHED_PERIODS:
+        description: >
+            The number of additional periods that will be allocated for strict
+            scheduling. The total # of periods allocated for strict scheduling
+            will be equal to the number of connections plus this number.
+        value: '0'
+
+    BLE_LL_USECS_PER_PERIOD:
+        description: >
+            The number of usecs per period.
+        value: '3250'
+
     # The number of random bytes to store
     BLE_LL_RNG_BUFSIZE:
         description: >


[2/2] incubator-mynewt-core git commit: Initial commit for the "body sensor network" code changes.

Posted by we...@apache.org.
Initial commit for the "body sensor network" code changes.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/631cc3d9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/631cc3d9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/631cc3d9

Branch: refs/heads/bsnbranch
Commit: 631cc3d93483079283f1f9094866b1123063a4ec
Parents: 63546ef
Author: William San Filippo <wi...@runtime.io>
Authored: Tue Mar 21 13:56:57 2017 -0700
Committer: William San Filippo <wi...@runtime.io>
Committed: Tue Mar 21 13:56:57 2017 -0700

----------------------------------------------------------------------
 apps/bsncent/pkg.yml                            |  35 +
 apps/bsncent/src/bsncent.h                      | 126 +++
 apps/bsncent/src/main.c                         | 459 +++++++++++
 apps/bsncent/src/misc.c                         | 209 +++++
 apps/bsncent/src/peer.c                         | 812 +++++++++++++++++++
 apps/bsncent/syscfg.yml                         |  26 +
 apps/bsnprph/pkg.yml                            |  42 +
 apps/bsnprph/src/bleprph.h                      |  60 ++
 apps/bsnprph/src/bsnprph.h                      |  62 ++
 apps/bsnprph/src/gatt_svr.c                     | 131 +++
 apps/bsnprph/src/main.c                         | 378 +++++++++
 apps/bsnprph/src/misc.c                         |  43 +
 apps/bsnprph/syscfg.yml                         |  35 +
 .../controller/include/controller/ble_ll_conn.h |   4 +
 .../include/controller/ble_ll_sched.h           |  34 +
 net/nimble/controller/src/ble_ll.c              |   3 +
 net/nimble/controller/src/ble_ll_conn.c         |  31 +-
 net/nimble/controller/src/ble_ll_sched.c        | 173 ++++
 net/nimble/controller/syscfg.yml                |  21 +
 19 files changed, 2683 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsncent/pkg.yml
----------------------------------------------------------------------
diff --git a/apps/bsncent/pkg.yml b/apps/bsncent/pkg.yml
new file mode 100644
index 0000000..6ce9462
--- /dev/null
+++ b/apps/bsncent/pkg.yml
@@ -0,0 +1,35 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+pkg.name: apps/bsncent
+pkg.type: app
+pkg.description: Simple BLE central application.
+pkg.author: "Apache Mynewt <de...@mynewt.incubator.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+    - kernel/os
+    - net/nimble/controller
+    - net/nimble/host
+    - net/nimble/host/services/gap
+    - net/nimble/host/services/gatt
+    - net/nimble/host/store/ram
+    - net/nimble/transport/ram
+    - sys/console/full
+    - sys/log/full
+    - sys/stats/full

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsncent/src/bsncent.h
----------------------------------------------------------------------
diff --git a/apps/bsncent/src/bsncent.h b/apps/bsncent/src/bsncent.h
new file mode 100644
index 0000000..d15a31c
--- /dev/null
+++ b/apps/bsncent/src/bsncent.h
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_BSNCENT_
+#define H_BSNCENT_
+
+#include "os/queue.h"
+#include "log/log.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ble_hs_adv_fields;
+struct ble_gap_conn_desc;
+struct ble_hs_cfg;
+union ble_store_value;
+union ble_store_key;
+
+extern struct log bsncent_log;
+
+/* bsncent uses the first "peruser" log module. */
+#define BSNCENT_LOG_MODULE  (LOG_MODULE_PERUSER + 0)
+
+/* Convenience macro for logging to the bsncent module. */
+#define BSNCENT_LOG(lvl, ...) \
+    LOG_ ## lvl(&bsncent_log, BSNCENT_LOG_MODULE, __VA_ARGS__)
+
+#define BSNCENT_SVC_GENDATA                 0x1811
+#define BSNCENT_CHR_SUP_NEW_ALERT_CAT_UUID  0x2A47
+#define BSNCENT_CHR_NEW_ALERT               0x2A46
+#define BSNCENT_CHR_SUP_UNR_ALERT_CAT_UUID  0x2A48
+#define BSNCENT_CHR_UNR_ALERT_STAT_UUID     0x2A45
+#define BSNCENT_CHR_ALERT_NOT_CTRL_PT       0x2A44
+
+/** GATT server. */
+void gatt_svr_register(void);
+void gatt_svr_init_cfg(struct ble_hs_cfg *cfg);
+
+
+/** Misc. */
+void print_bytes(const uint8_t *bytes, int len);
+void print_mbuf(const struct os_mbuf *om);
+char *addr_str(const void *addr);
+void print_uuid(const ble_uuid_t *uuid);
+void print_conn_desc(const struct ble_gap_conn_desc *desc);
+void print_adv_fields(const struct ble_hs_adv_fields *fields);
+
+/** Peer. */
+struct peer_dsc {
+    SLIST_ENTRY(peer_dsc) next;
+    struct ble_gatt_dsc dsc;
+};
+SLIST_HEAD(peer_dsc_list, peer_dsc);
+
+struct peer_chr {
+    SLIST_ENTRY(peer_chr) next;
+    struct ble_gatt_chr chr;
+
+    struct peer_dsc_list dscs;
+};
+SLIST_HEAD(peer_chr_list, peer_chr);
+
+struct peer_svc {
+    SLIST_ENTRY(peer_svc) next;
+    struct ble_gatt_svc svc;
+
+    struct peer_chr_list chrs;
+};
+SLIST_HEAD(peer_svc_list, peer_svc);
+
+struct peer;
+typedef void peer_disc_fn(const struct peer *peer, int status, void *arg);
+
+struct peer {
+    SLIST_ENTRY(peer) next;
+
+    uint16_t conn_handle;
+
+    /** List of discovered GATT services. */
+    struct peer_svc_list svcs;
+
+    /** Keeps track of where we are in the service discovery process. */
+    uint16_t disc_prev_chr_val;
+    struct peer_svc *cur_svc;
+
+    /** Callback that gets executed when service discovery completes. */
+    peer_disc_fn *disc_cb;
+    void *disc_cb_arg;
+};
+
+int peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb,
+                  void *disc_cb_arg);
+const struct peer_dsc *
+peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
+                   const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid);
+const struct peer_chr *
+peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
+                   const ble_uuid_t *chr_uuid);
+const struct peer_svc *
+peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid);
+int peer_delete(uint16_t conn_handle);
+int peer_add(uint16_t conn_handle);
+int peer_count(void);
+int peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsncent/src/main.c
----------------------------------------------------------------------
diff --git a/apps/bsncent/src/main.c b/apps/bsncent/src/main.c
new file mode 100755
index 0000000..ed89946
--- /dev/null
+++ b/apps/bsncent/src/main.c
@@ -0,0 +1,459 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "syscfg/syscfg.h"
+#include "sysinit/sysinit.h"
+#include "bsp/bsp.h"
+#include "os/os.h"
+
+/* BLE */
+#include "nimble/ble.h"
+#include "controller/ble_ll.h"
+#include "host/ble_hs.h"
+
+/* RAM HCI transport. */
+#include "transport/ram/ble_hci_ram.h"
+
+/* Mandatory services. */
+#include "services/gap/ble_svc_gap.h"
+#include "services/gatt/ble_svc_gatt.h"
+
+/* Application-specified header. */
+#include "bsncent.h"
+
+#define BSNCENT_PRINT_RATE      (OS_TICKS_PER_SEC * 10)
+
+struct log bsncent_log;
+
+static uint32_t num_notify_pkts_rx;
+static uint32_t num_notify_bytes_rx;
+
+/* Prints statistics every 10 seconds. */
+static struct os_callout bsncent_print_timer;
+
+/* c66f3301-33b3-4687-850a-d52b0d5d1e3c */
+static const ble_uuid128_t bsncent_svc_gendata_uuid =
+    BLE_UUID128_INIT(0x3c, 0x1e, 0x5d, 0x0d, 0x2b, 0xd5, 0x0a, 0x85,
+                     0x87, 0x46, 0xb3, 0x33, 0x01, 0x33, 0x6f, 0xc6);
+
+/* c66f3301-33b3-4687-850a-d52b0d5d1e3d */
+static const ble_uuid128_t bsncent_chr_gendata_uuid =
+    BLE_UUID128_INIT(0x3d, 0x1e, 0x5d, 0x0d, 0x2b, 0xd5, 0x0a, 0x85,
+                     0x87, 0x46, 0xb3, 0x33, 0x01, 0x33, 0x6f, 0xc6);
+
+static const uint8_t bsncent_cent_public_addr[6] = {
+    0x0a, 0x0b, 0x09, 0x09, 0x09, 0x00,
+};
+
+static const ble_addr_t bsncent_peer_addrs[] = {
+    { BLE_ADDR_PUBLIC, { 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x01 } },
+    { BLE_ADDR_PUBLIC, { 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x02 } },
+    { BLE_ADDR_PUBLIC, { 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x03 } },
+    { BLE_ADDR_PUBLIC, { 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x04 } },
+    { BLE_ADDR_PUBLIC, { 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x05 } },
+};
+static const int bsncent_num_peer_addrs =
+    sizeof bsncent_peer_addrs / sizeof bsncent_peer_addrs[0];
+
+static int bsncent_gap_event(struct ble_gap_event *event, void *arg);
+
+static const struct ble_gap_conn_params ble_gap_conn_params_bsn = {
+   .scan_itvl = 0x0010,
+   .scan_window = 0x0010,
+   .itvl_min = 13,
+   .itvl_max = 13,
+   .latency = BLE_GAP_INITIAL_CONN_LATENCY,
+   .supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT,
+   .min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN,
+   .max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN,
+};
+
+/**
+ * Application callback.  Called when the attempt to subscribe to notifications
+ * for the ANS Unread Alert Status characteristic has completed.
+ */
+static int
+bsncent_on_subscribe(uint16_t conn_handle,
+                     const struct ble_gatt_error *error,
+                     struct ble_gatt_attr *attr,
+                     void *arg)
+{
+    BSNCENT_LOG(INFO, "Subscribe complete; status=%d conn_handle=%d "
+                      "attr_handle=%d\n",
+                error->status, conn_handle, attr->handle);
+
+    return 0;
+}
+
+/**
+ * Performs three concurrent GATT operations against the specified peer:
+ * 1. Reads the ANS Supported New Alert Category characteristic.
+ * 2. Writes the ANS Alert Notification Control Point characteristic.
+ * 3. Subscribes to notifications for the ANS Unread Alert Status
+ *    characteristic.
+ *
+ * If the peer does not support a required service, characteristic, or
+ * descriptor, then the peer lied when it claimed support for the alert
+ * notification service!  When this happens, or if a GATT procedure fails,
+ * this function immediately terminates the connection.
+ */
+static void
+bsncent_subscribe(const struct peer *peer)
+{
+    const struct peer_dsc *dsc;
+    uint8_t value[2];
+    int rc;
+
+    /* Subscribe to notifications for the gendata characteristic.
+     * A central enables notifications by writing two bytes (1, 0) to the
+     * characteristic's client-characteristic-configuration-descriptor (CCCD).
+     */
+    dsc = peer_dsc_find_uuid(
+        peer,
+        &bsncent_svc_gendata_uuid.u,
+        &bsncent_chr_gendata_uuid.u,
+        BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16));
+    if (dsc == NULL) {
+        BSNCENT_LOG(ERROR, "Error: Peer lacks a CCCD for the generic data "
+                           "characteristic\n");
+        goto err;
+    }
+
+    value[0] = 1;
+    value[1] = 0;
+    rc = ble_gattc_write_flat(peer->conn_handle, dsc->dsc.handle,
+                              value, sizeof value, bsncent_on_subscribe, NULL);
+    if (rc != 0) {
+        BSNCENT_LOG(ERROR, "Error: Failed to subscribe to characteristic; "
+                           "rc=%d\n", rc);
+        goto err;
+    }
+
+    return;
+
+err:
+    /* Terminate the connection. */
+    ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
+}
+
+/**
+ * Called when service discovery of the specified peer has completed.
+ */
+static void
+bsncent_on_disc_complete(const struct peer *peer, int status, void *arg)
+{
+
+    if (status != 0) {
+        /* Service discovery failed.  Terminate the connection. */
+        BSNCENT_LOG(ERROR, "Error: Service discovery failed; status=%d "
+                           "conn_handle=%d\n", status, peer->conn_handle);
+        ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
+        return;
+    }
+
+    /* Service discovery has completed successfully.  Now we have a complete
+     * list of services, characteristics, and descriptors that the peer
+     * supports.
+     */
+    BSNCENT_LOG(ERROR, "Service discovery complete; status=%d "
+                       "conn_handle=%d\n", status, peer->conn_handle);
+
+    /* Now subscribe to the gendata characterustic. */
+    bsncent_subscribe(peer);
+}
+
+
+static int
+bsncent_on_mtu_exchanged(uint16_t conn_handle,
+                         const struct ble_gatt_error *error,
+                         uint16_t mtu, void *arg)
+{
+    int rc;
+
+    if (error->status != 0) {
+        BSNCENT_LOG(ERROR, "MTU exchange failed; rc=%d\n", error->status);
+        ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
+        return 0;
+    }
+
+    /* Perform service discovery. */
+    rc = peer_disc_all(conn_handle, bsncent_on_disc_complete, NULL);
+    if (rc != 0) {
+        BSNCENT_LOG(ERROR, "Failed to discover services; rc=%d\n", rc);
+        ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
+        return 0;
+    }
+
+    return 0;
+}
+
+static void
+bsncent_connect(void)
+{
+    int rc;
+
+    rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER,
+                         &ble_gap_conn_params_bsn, bsncent_gap_event, NULL);
+    if (rc != 0) {
+        BSNCENT_LOG(ERROR, "Error connecting; rc=%d\n", rc);
+        if (!((rc == BLE_HS_EALREADY) || (rc == BLE_HS_EBUSY))) {
+            /* Only assert if we are not already trying */
+            assert(0);
+        }
+    }
+}
+
+static void
+bsncent_fill_wl(void)
+{
+    int rc;
+
+    rc = ble_gap_wl_set(bsncent_peer_addrs, bsncent_num_peer_addrs);
+    if (rc != 0) {
+        BSNCENT_LOG(ERROR, "Error setting white list; rc=%d\n", rc);
+        assert(0);
+    }
+}
+
+static void
+bsncent_print_timer_reset(void)
+{
+    int rc;
+
+    rc = os_callout_reset(&bsncent_print_timer, BSNCENT_PRINT_RATE);
+    assert(rc == 0);
+}
+
+/**
+ * Prints statistics every 10 seconds.
+ */
+static void
+bsncent_print_timer_exp(struct os_event *ev)
+{
+    static uint32_t prev_bytes;
+    static uint32_t prev_pkts;
+    uint32_t diff_bytes;
+    uint32_t diff_pkts;
+
+    console_printf("--\n");
+    console_printf("%8d connections\n", peer_count());
+    console_printf("%8lu packets received\n",
+                   (unsigned long)num_notify_pkts_rx);
+    console_printf("%8lu bytes received\n",
+                   (unsigned long)num_notify_bytes_rx);
+
+    if (prev_pkts != 0) {
+        diff_pkts = num_notify_pkts_rx - prev_pkts;
+        diff_bytes = num_notify_bytes_rx - prev_bytes;
+
+        console_printf("%8lu packets per second\n",
+                       (unsigned long)(diff_pkts /
+                                       (BSNCENT_PRINT_RATE /
+                                        OS_TICKS_PER_SEC)));
+        console_printf("%8lu bytes per second\n",
+                       (unsigned long)(diff_bytes /
+                                       (BSNCENT_PRINT_RATE /
+                                        OS_TICKS_PER_SEC)));
+    }
+
+    prev_pkts = num_notify_pkts_rx;
+    prev_bytes = num_notify_bytes_rx;
+
+    bsncent_print_timer_reset();
+}
+
+/**
+ * The nimble host executes this callback when a GAP event occurs.  The
+ * application associates a GAP event callback with each connection that is
+ * established.  bsncent uses the same callback for all connections.
+ *
+ * @param event                 The event being signalled.
+ * @param arg                   Application-specified argument; unused by
+ *                                  bsncent.
+ *
+ * @return                      0 if the application successfully handled the
+ *                                  event; nonzero on failure.  The semantics
+ *                                  of the return code is specific to the
+ *                                  particular GAP event being signalled.
+ */
+static int
+bsncent_gap_event(struct ble_gap_event *event, void *arg)
+{
+    struct ble_gap_conn_desc desc;
+    int rc;
+
+    switch (event->type) {
+    case BLE_GAP_EVENT_CONNECT:
+        /* A new connection was established or a connection attempt failed. */
+        if (event->connect.status == 0) {
+            /* Connection successfully established. */
+            BSNCENT_LOG(INFO, "Connection established ");
+
+            rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+            assert(rc == 0);
+            print_conn_desc(&desc);
+            BSNCENT_LOG(INFO, "\n");
+
+            /* Remember peer. */
+            rc = peer_add(event->connect.conn_handle);
+            if (rc != 0) {
+                BSNCENT_LOG(ERROR, "Failed to add peer; rc=%d\n", rc);
+                assert(0);
+            }
+
+            /* Try to connect to next peer if any remain unconnected. */
+            if (peer_count() < bsncent_num_peer_addrs) {
+                bsncent_connect();
+            }
+
+            /* Negotiate ATT MTU. */
+            rc = ble_gattc_exchange_mtu(event->connect.conn_handle,
+                                        bsncent_on_mtu_exchanged, NULL);
+            if (rc != 0) {
+                BSNCENT_LOG(ERROR, "Failed to exchange MTU; rc=%d\n", rc);
+                return 0;
+            }
+        } else {
+            /* Connection attempt failed; resume connecting. */
+            BSNCENT_LOG(ERROR, "Error: Connection failed; status=%d\n",
+                        event->connect.status);
+            bsncent_connect();
+        }
+
+        return 0;
+
+    case BLE_GAP_EVENT_DISCONNECT:
+        /* Connection terminated. */
+        BSNCENT_LOG(INFO, "disconnect; reason=%d ", event->disconnect.reason);
+        print_conn_desc(&event->disconnect.conn);
+        BSNCENT_LOG(INFO, "\n");
+
+        /* Forget about peer. */
+        peer_delete(event->disconnect.conn.conn_handle);
+
+        /* Resume scanning. */
+        bsncent_connect();
+        return 0;
+
+    case BLE_GAP_EVENT_ENC_CHANGE:
+        /* Encryption has been enabled or disabled for this connection. */
+        BSNCENT_LOG(INFO, "encryption change event; status=%d ",
+                    event->enc_change.status);
+        rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
+        assert(rc == 0);
+        print_conn_desc(&desc);
+        return 0;
+
+    case BLE_GAP_EVENT_NOTIFY_RX:
+        /* Peer sent us a notification or indication. */
+        BSNCENT_LOG(DEBUG, "received %s; conn_handle=%d attr_handle=%d "
+                           "attr_len=%d\n",
+                    event->notify_rx.indication ?
+                        "indication" :
+                        "notification",
+                    event->notify_rx.conn_handle,
+                    event->notify_rx.attr_handle,
+                    OS_MBUF_PKTLEN(event->notify_rx.om));
+
+        num_notify_pkts_rx++;
+        num_notify_bytes_rx += OS_MBUF_PKTLEN(event->notify_rx.om);
+
+        /* Attribute data is contained in event->notify_rx.attr_data. */
+        return 0;
+
+    case BLE_GAP_EVENT_MTU:
+        BSNCENT_LOG(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
+                    event->mtu.conn_handle,
+                    event->mtu.channel_id,
+                    event->mtu.value);
+        return 0;
+
+    default:
+        return 0;
+    }
+}
+
+static void
+bsncent_on_reset(int reason)
+{
+    BSNCENT_LOG(ERROR, "Resetting state; reason=%d\n", reason);
+}
+
+static void
+bsncent_on_sync(void)
+{
+    /* Start printing statistics. */
+    bsncent_print_timer_reset();
+
+    /* Add the five known peripherals to the white list. */
+    bsncent_fill_wl();
+
+    /* Attempt to connect to the first peripheral. */
+    bsncent_connect();
+}
+
+/**
+ * main
+ *
+ * All application logic and NimBLE host work is performed in default task.
+ *
+ * @return int NOTE: this function should never return!
+ */
+int
+main(void)
+{
+    int rc;
+
+    /* Set initial BLE device address. */
+    memcpy(g_dev_addr, bsncent_cent_public_addr, 6);
+
+    /* Initialize OS */
+    sysinit();
+
+    /* Initialize the bsncent log. */
+    log_register("bsncent", &bsncent_log, &log_console_handler, NULL,
+                 LOG_SYSLEVEL);
+
+    /* Configure the host. */
+    log_register("ble_hs", &ble_hs_log, &log_console_handler, NULL,
+                 LOG_SYSLEVEL);
+    ble_hs_cfg.reset_cb = bsncent_on_reset;
+    ble_hs_cfg.sync_cb = bsncent_on_sync;
+
+    os_callout_init(&bsncent_print_timer, os_eventq_dflt_get(),
+                    bsncent_print_timer_exp, NULL);
+
+    /* XXX: I think some of these need to be based on # of connections */
+    /* Initialize data structures to track connected peers. */
+    rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 96, 64);
+    assert(rc == 0);
+
+    /* Set the default device name. */
+    rc = ble_svc_gap_device_name_set(MYNEWT_VAL(BSNCENT_BLE_NAME));
+    assert(rc == 0);
+
+    /* os start should never return. If it does, this should be an error */
+    while (1) {
+        os_eventq_run(os_eventq_dflt_get());
+    }
+
+    return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsncent/src/misc.c
----------------------------------------------------------------------
diff --git a/apps/bsncent/src/misc.c b/apps/bsncent/src/misc.c
new file mode 100644
index 0000000..910a304
--- /dev/null
+++ b/apps/bsncent/src/misc.c
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include "host/ble_hs.h"
+#include "host/ble_uuid.h"
+#include "bsncent.h"
+
+/**
+ * Utility function to log an array of bytes.
+ */
+void
+print_bytes(const uint8_t *bytes, int len)
+{
+    int i;
+
+    for (i = 0; i < len; i++) {
+        BSNCENT_LOG(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
+    }
+}
+
+void
+print_mbuf(const struct os_mbuf *om)
+{
+    int colon;
+
+    colon = 0;
+    while (om != NULL) {
+        if (colon) {
+            BSNCENT_LOG(DEBUG, ":");
+        } else {
+            colon = 1;
+        }
+        print_bytes(om->om_data, om->om_len);
+        om = SLIST_NEXT(om, om_next);
+    }
+}
+
+char *
+addr_str(const void *addr)
+{
+    static char buf[6 * 2 + 5 + 1];
+    const uint8_t *u8p;
+
+    u8p = addr;
+    sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
+            u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
+
+    return buf;
+}
+
+void
+print_uuid(const ble_uuid_t *uuid)
+{
+    char buf[BLE_UUID_STR_LEN];
+
+    BSNCENT_LOG(DEBUG, "%s", ble_uuid_to_str(uuid, buf));
+}
+
+/**
+ * Logs information about a connection to the console.
+ */
+void
+print_conn_desc(const struct ble_gap_conn_desc *desc)
+{
+    BSNCENT_LOG(DEBUG, "handle=%d our_ota_addr_type=%d our_ota_addr=%s ",
+                desc->conn_handle, desc->our_ota_addr.type,
+                addr_str(desc->our_ota_addr.val));
+    BSNCENT_LOG(DEBUG, "our_id_addr_type=%d our_id_addr=%s ",
+                desc->our_id_addr.type, addr_str(desc->our_id_addr.val));
+    BSNCENT_LOG(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ",
+                desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val));
+    BSNCENT_LOG(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ",
+                desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val));
+    BSNCENT_LOG(DEBUG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d "
+                "encrypted=%d authenticated=%d bonded=%d",
+                desc->conn_itvl, desc->conn_latency,
+                desc->supervision_timeout,
+                desc->sec_state.encrypted,
+                desc->sec_state.authenticated,
+                desc->sec_state.bonded);
+}
+
+
+void
+print_adv_fields(const struct ble_hs_adv_fields *fields)
+{
+    char s[BLE_HS_ADV_MAX_SZ];
+    const uint8_t *u8p;
+    int i;
+
+    if (fields->flags != 0) {
+        BSNCENT_LOG(DEBUG, "    flags=0x%02x\n", fields->flags);
+    }
+
+    if (fields->uuids16 != NULL) {
+        BSNCENT_LOG(DEBUG, "    uuids16(%scomplete)=",
+                    fields->uuids16_is_complete ? "" : "in");
+        for (i = 0; i < fields->num_uuids16; i++) {
+            print_uuid(&fields->uuids16[i].u);
+            BSNCENT_LOG(DEBUG, " ");
+        }
+        BSNCENT_LOG(DEBUG, "\n");
+    }
+
+    if (fields->uuids32 != NULL) {
+        BSNCENT_LOG(DEBUG, "    uuids32(%scomplete)=",
+                    fields->uuids32_is_complete ? "" : "in");
+        for (i = 0; i < fields->num_uuids32; i++) {
+            print_uuid(&fields->uuids32[i].u);
+            BSNCENT_LOG(DEBUG, " ");
+        }
+        BSNCENT_LOG(DEBUG, "\n");
+    }
+
+    if (fields->uuids128 != NULL) {
+        BSNCENT_LOG(DEBUG, "    uuids128(%scomplete)=",
+                    fields->uuids128_is_complete ? "" : "in");
+        for (i = 0; i < fields->num_uuids128; i++) {
+            print_uuid(&fields->uuids128[i].u);
+            BSNCENT_LOG(DEBUG, " ");
+        }
+        BSNCENT_LOG(DEBUG, "\n");
+    }
+
+    if (fields->name != NULL) {
+        assert(fields->name_len < sizeof s - 1);
+        memcpy(s, fields->name, fields->name_len);
+        s[fields->name_len] = '\0';
+        BSNCENT_LOG(DEBUG, "    name(%scomplete)=%s\n",
+                    fields->name_is_complete ? "" : "in", s);
+    }
+
+    if (fields->tx_pwr_lvl_is_present) {
+        BSNCENT_LOG(DEBUG, "    tx_pwr_lvl=%d\n", fields->tx_pwr_lvl);
+    }
+
+    if (fields->slave_itvl_range != NULL) {
+        BSNCENT_LOG(DEBUG, "    slave_itvl_range=");
+        print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
+        BSNCENT_LOG(DEBUG, "\n");
+    }
+
+    if (fields->svc_data_uuid16 != NULL) {
+        BSNCENT_LOG(DEBUG, "    svc_data_uuid16=");
+        print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len);
+        BSNCENT_LOG(DEBUG, "\n");
+    }
+
+    if (fields->public_tgt_addr != NULL) {
+        BSNCENT_LOG(DEBUG, "    public_tgt_addr=");
+        u8p = fields->public_tgt_addr;
+        for (i = 0; i < fields->num_public_tgt_addrs; i++) {
+            BSNCENT_LOG(DEBUG, "public_tgt_addr=%s ", addr_str(u8p));
+            u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
+        }
+        BSNCENT_LOG(DEBUG, "\n");
+    }
+
+    if (fields->appearance_is_present) {
+        BSNCENT_LOG(DEBUG, "    appearance=0x%04x\n", fields->appearance);
+    }
+
+    if (fields->adv_itvl_is_present) {
+        BSNCENT_LOG(DEBUG, "    adv_itvl=0x%04x\n", fields->adv_itvl);
+    }
+
+    if (fields->svc_data_uuid32 != NULL) {
+        BSNCENT_LOG(DEBUG, "    svc_data_uuid32=");
+        print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len);
+        BSNCENT_LOG(DEBUG, "\n");
+    }
+
+    if (fields->svc_data_uuid128 != NULL) {
+        BSNCENT_LOG(DEBUG, "    svc_data_uuid128=");
+        print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len);
+        BSNCENT_LOG(DEBUG, "\n");
+    }
+
+    if (fields->uri != NULL) {
+        BSNCENT_LOG(DEBUG, "    uri=");
+        print_bytes(fields->uri, fields->uri_len);
+        BSNCENT_LOG(DEBUG, "\n");
+    }
+
+    if (fields->mfg_data != NULL) {
+        BSNCENT_LOG(DEBUG, "    mfg_data=");
+        print_bytes(fields->mfg_data, fields->mfg_data_len);
+        BSNCENT_LOG(DEBUG, "\n");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsncent/src/peer.c
----------------------------------------------------------------------
diff --git a/apps/bsncent/src/peer.c b/apps/bsncent/src/peer.c
new file mode 100644
index 0000000..0e4f7f4
--- /dev/null
+++ b/apps/bsncent/src/peer.c
@@ -0,0 +1,812 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "host/ble_hs.h"
+#include "bsncent.h"
+
+static void *peer_svc_mem;
+static struct os_mempool peer_svc_pool;
+
+static void *peer_chr_mem;
+static struct os_mempool peer_chr_pool;
+
+static void *peer_dsc_mem;
+static struct os_mempool peer_dsc_pool;
+
+static void *peer_mem;
+static struct os_mempool peer_pool;
+static SLIST_HEAD(, peer) peers;
+
+static struct peer_svc *
+peer_svc_find_range(struct peer *peer, uint16_t attr_handle);
+static struct peer_svc *
+peer_svc_find(struct peer *peer, uint16_t svc_start_handle,
+              struct peer_svc **out_prev);
+int
+peer_svc_is_empty(const struct peer_svc *svc);
+
+uint16_t
+chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr);
+int
+chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr);
+static struct peer_chr *
+peer_chr_find(const struct peer_svc *svc, uint16_t chr_def_handle,
+              struct peer_chr **out_prev);
+static void
+peer_disc_chrs(struct peer *peer);
+
+static int
+peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
+                uint16_t chr_def_handle, const struct ble_gatt_dsc *dsc,
+                void *arg);
+
+static struct peer *
+peer_find(uint16_t conn_handle)
+{
+    struct peer *peer;
+
+    SLIST_FOREACH(peer, &peers, next) {
+        if (peer->conn_handle == conn_handle) {
+            return peer;
+        }
+    }
+
+    return NULL;
+}
+
+static void
+peer_disc_complete(struct peer *peer, int rc)
+{
+    peer->disc_prev_chr_val = 0;
+
+    /* Notify caller that discovery has completed. */
+    if (peer->disc_cb != NULL) {
+        peer->disc_cb(peer, rc, peer->disc_cb_arg);
+    }
+}
+
+static struct peer_dsc *
+peer_dsc_find_prev(const struct peer_chr *chr, uint16_t dsc_handle)
+{
+    struct peer_dsc *prev;
+    struct peer_dsc *dsc;
+
+    prev = NULL;
+    SLIST_FOREACH(dsc, &chr->dscs, next) {
+        if (dsc->dsc.handle >= dsc_handle) {
+            break;
+        }
+
+        prev = dsc;
+    }
+
+    return prev;
+}
+
+static struct peer_dsc *
+peer_dsc_find(const struct peer_chr *chr, uint16_t dsc_handle,
+              struct peer_dsc **out_prev)
+{
+    struct peer_dsc *prev;
+    struct peer_dsc *dsc;
+
+    prev = peer_dsc_find_prev(chr, dsc_handle);
+    if (prev == NULL) {
+        dsc = SLIST_FIRST(&chr->dscs);
+    } else {
+        dsc = SLIST_NEXT(prev, next);
+    }
+
+    if (dsc != NULL && dsc->dsc.handle != dsc_handle) {
+        dsc = NULL;
+    }
+
+    if (out_prev != NULL) {
+        *out_prev = prev;
+    }
+    return dsc;
+}
+
+static int
+peer_dsc_add(struct peer *peer, uint16_t chr_val_handle,
+             const struct ble_gatt_dsc *gatt_dsc)
+{
+    struct peer_dsc *prev;
+    struct peer_dsc *dsc;
+    struct peer_svc *svc;
+    struct peer_chr *chr;
+
+    svc = peer_svc_find_range(peer, chr_val_handle);
+    if (svc == NULL) {
+        /* Can't find service for discovered descriptor; this shouldn't
+         * happen.
+         */
+        assert(0);
+        return BLE_HS_EUNKNOWN;
+    }
+
+    chr = peer_chr_find(svc, chr_val_handle, NULL);
+    if (chr == NULL) {
+        /* Can't find characteristic for discovered descriptor; this shouldn't
+         * happen.
+         */
+        assert(0);
+        return BLE_HS_EUNKNOWN;
+    }
+
+    dsc = peer_dsc_find(chr, gatt_dsc->handle, &prev);
+    if (dsc != NULL) {
+        /* Descriptor already discovered. */
+        return 0;
+    }
+
+    dsc = os_memblock_get(&peer_dsc_pool);
+    if (dsc == NULL) {
+        /* Out of memory. */
+        return BLE_HS_ENOMEM;
+    }
+    memset(dsc, 0, sizeof *dsc);
+
+    dsc->dsc = *gatt_dsc;
+
+    if (prev == NULL) {
+        SLIST_INSERT_HEAD(&chr->dscs, dsc, next);
+    } else {
+        SLIST_NEXT(prev, next) = dsc;
+    }
+
+    return 0;
+}
+
+static void
+peer_disc_dscs(struct peer *peer)
+{
+    struct peer_chr *chr;
+    struct peer_svc *svc;
+    int rc;
+
+    /* Search through the list of discovered characteristics for the first
+     * characteristic that contains undiscovered descriptors.  Then, discover
+     * all descriptors belonging to that characteristic.
+     */
+    SLIST_FOREACH(svc, &peer->svcs, next) {
+        SLIST_FOREACH(chr, &svc->chrs, next) {
+            if (!chr_is_empty(svc, chr) &&
+                SLIST_EMPTY(&chr->dscs) &&
+                peer->disc_prev_chr_val <= chr->chr.def_handle) {
+
+                rc = ble_gattc_disc_all_dscs(peer->conn_handle,
+                                             chr->chr.val_handle,
+                                             chr_end_handle(svc, chr),
+                                             peer_dsc_disced, peer);
+                if (rc != 0) {
+                    peer_disc_complete(peer, rc);
+                }
+
+                peer->disc_prev_chr_val = chr->chr.val_handle;
+                return;
+            }
+        }
+    }
+
+    /* All descriptors discovered. */
+    peer_disc_complete(peer, 0);
+}
+
+static int
+peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
+                uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
+                void *arg)
+{
+    struct peer *peer;
+    int rc;
+
+    peer = arg;
+    assert(peer->conn_handle == conn_handle);
+
+    switch (error->status) {
+    case 0:
+        rc = peer_dsc_add(peer, chr_val_handle, dsc);
+        break;
+
+    case BLE_HS_EDONE:
+        /* All descriptors in this characteristic discovered; start discovering
+         * descriptors in the next characteristic.
+         */
+        if (peer->disc_prev_chr_val > 0) {
+            peer_disc_dscs(peer);
+        }
+        rc = 0;
+        break;
+
+    default:
+        /* Error; abort discovery. */
+        rc = error->status;
+        break;
+    }
+
+    if (rc != 0) {
+        /* Error; abort discovery. */
+        peer_disc_complete(peer, rc);
+    }
+
+    return rc;
+}
+
+uint16_t
+chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr)
+{
+    const struct peer_chr *next_chr;
+
+    next_chr = SLIST_NEXT(chr, next);
+    if (next_chr != NULL) {
+        return next_chr->chr.def_handle - 1;
+    } else {
+        return svc->svc.end_handle;
+    }
+}
+
+int
+chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr)
+{
+    return chr_end_handle(svc, chr) <= chr->chr.val_handle;
+}
+
+static struct peer_chr *
+peer_chr_find_prev(const struct peer_svc *svc, uint16_t chr_val_handle)
+{
+    struct peer_chr *prev;
+    struct peer_chr *chr;
+
+    prev = NULL;
+    SLIST_FOREACH(chr, &svc->chrs, next) {
+        if (chr->chr.val_handle >= chr_val_handle) {
+            break;
+        }
+
+        prev = chr;
+    }
+
+    return prev;
+}
+
+static struct peer_chr *
+peer_chr_find(const struct peer_svc *svc, uint16_t chr_val_handle,
+              struct peer_chr **out_prev)
+{
+    struct peer_chr *prev;
+    struct peer_chr *chr;
+
+    prev = peer_chr_find_prev(svc, chr_val_handle);
+    if (prev == NULL) {
+        chr = SLIST_FIRST(&svc->chrs);
+    } else {
+        chr = SLIST_NEXT(prev, next);
+    }
+
+    if (chr != NULL && chr->chr.val_handle != chr_val_handle) {
+        chr = NULL;
+    }
+
+    if (out_prev != NULL) {
+        *out_prev = prev;
+    }
+    return chr;
+}
+
+static void
+peer_chr_delete(struct peer_chr *chr)
+{
+    struct peer_dsc *dsc;
+
+    while ((dsc = SLIST_FIRST(&chr->dscs)) != NULL) {
+        SLIST_REMOVE_HEAD(&chr->dscs, next);
+        os_memblock_put(&peer_dsc_pool, dsc);
+    }
+
+    os_memblock_put(&peer_chr_pool, chr);
+}
+
+static int
+peer_chr_add(struct peer *peer,  uint16_t svc_start_handle,
+             const struct ble_gatt_chr *gatt_chr)
+{
+    struct peer_chr *prev;
+    struct peer_chr *chr;
+    struct peer_svc *svc;
+
+    svc = peer_svc_find(peer, svc_start_handle, NULL);
+    if (svc == NULL) {
+        /* Can't find service for discovered characteristic; this shouldn't
+         * happen.
+         */
+        assert(0);
+        return BLE_HS_EUNKNOWN;
+    }
+
+    chr = peer_chr_find(svc, gatt_chr->def_handle, &prev);
+    if (chr != NULL) {
+        /* Characteristic already discovered. */
+        return 0;
+    }
+
+    chr = os_memblock_get(&peer_chr_pool);
+    if (chr == NULL) {
+        /* Out of memory. */
+        return BLE_HS_ENOMEM;
+    }
+    memset(chr, 0, sizeof *chr);
+
+    chr->chr = *gatt_chr;
+
+    if (prev == NULL) {
+        SLIST_INSERT_HEAD(&svc->chrs, chr, next);
+    } else {
+        SLIST_NEXT(prev, next) = chr;
+    }
+
+    return 0;
+}
+
+static int
+peer_chr_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
+                const struct ble_gatt_chr *chr, void *arg)
+{
+    struct peer *peer;
+    int rc;
+
+    peer = arg;
+    assert(peer->conn_handle == conn_handle);
+
+    switch (error->status) {
+    case 0:
+        rc = peer_chr_add(peer, peer->cur_svc->svc.start_handle, chr);
+        break;
+
+    case BLE_HS_EDONE:
+        /* All characteristics in this service discovered; start discovering
+         * characteristics in the next service.
+         */
+        if (peer->disc_prev_chr_val > 0) {
+             peer_disc_chrs(peer);
+        }
+        rc = 0;
+        break;
+
+    default:
+        rc = error->status;
+        break;
+    }
+
+    if (rc != 0) {
+        /* Error; abort discovery. */
+        peer_disc_complete(peer, rc);
+    }
+
+    return rc;
+}
+
+static void
+peer_disc_chrs(struct peer *peer)
+{
+    struct peer_svc *svc;
+    int rc;
+
+    /* Search through the list of discovered service for the first service that
+     * contains undiscovered characteristics.  Then, discover all
+     * characteristics belonging to that service.
+     */
+    SLIST_FOREACH(svc, &peer->svcs, next) {
+        if (!peer_svc_is_empty(svc) && SLIST_EMPTY(&svc->chrs)) {
+            peer->cur_svc = svc;
+            rc = ble_gattc_disc_all_chrs(peer->conn_handle,
+                                         svc->svc.start_handle,
+                                         svc->svc.end_handle,
+                                         peer_chr_disced, peer);
+            if (rc != 0) {
+                peer_disc_complete(peer, rc);
+            }
+            return;
+        }
+    }
+
+    /* All characteristics discovered. */
+    peer_disc_dscs(peer);
+}
+
+int
+peer_svc_is_empty(const struct peer_svc *svc)
+{
+    return svc->svc.end_handle < svc->svc.start_handle;
+}
+
+static struct peer_svc *
+peer_svc_find_prev(struct peer *peer, uint16_t svc_start_handle)
+{
+    struct peer_svc *prev;
+    struct peer_svc *svc;
+
+    prev = NULL;
+    SLIST_FOREACH(svc, &peer->svcs, next) {
+        if (svc->svc.start_handle >= svc_start_handle) {
+            break;
+        }
+
+        prev = svc;
+    }
+
+    return prev;
+}
+
+static struct peer_svc *
+peer_svc_find(struct peer *peer, uint16_t svc_start_handle,
+              struct peer_svc **out_prev)
+{
+    struct peer_svc *prev;
+    struct peer_svc *svc;
+
+    prev = peer_svc_find_prev(peer, svc_start_handle);
+    if (prev == NULL) {
+        svc = SLIST_FIRST(&peer->svcs);
+    } else {
+        svc = SLIST_NEXT(prev, next);
+    }
+
+    if (svc != NULL && svc->svc.start_handle != svc_start_handle) {
+        svc = NULL;
+    }
+
+    if (out_prev != NULL) {
+        *out_prev = prev;
+    }
+    return svc;
+}
+
+static struct peer_svc *
+peer_svc_find_range(struct peer *peer, uint16_t attr_handle)
+{
+    struct peer_svc *svc;
+
+    SLIST_FOREACH(svc, &peer->svcs, next) {
+        if (svc->svc.start_handle <= attr_handle &&
+            svc->svc.end_handle >= attr_handle) {
+
+            return svc;
+        }
+    }
+
+    return NULL;
+}
+
+const struct peer_svc *
+peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid)
+{
+    const struct peer_svc *svc;
+
+    SLIST_FOREACH(svc, &peer->svcs, next) {
+        if (ble_uuid_cmp(&svc->svc.uuid.u, uuid) == 0) {
+            return svc;
+        }
+    }
+
+    return NULL;
+}
+
+const struct peer_chr *
+peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
+                   const ble_uuid_t *chr_uuid)
+{
+    const struct peer_svc *svc;
+    const struct peer_chr *chr;
+
+    svc = peer_svc_find_uuid(peer, svc_uuid);
+    if (svc == NULL) {
+        return NULL;
+    }
+
+    SLIST_FOREACH(chr, &svc->chrs, next) {
+        if (ble_uuid_cmp(&chr->chr.uuid.u, chr_uuid) == 0) {
+            return chr;
+        }
+    }
+
+    return NULL;
+}
+
+const struct peer_dsc *
+peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
+                   const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid)
+{
+    const struct peer_chr *chr;
+    const struct peer_dsc *dsc;
+
+    chr = peer_chr_find_uuid(peer, svc_uuid, chr_uuid);
+    if (chr == NULL) {
+        return NULL;
+    }
+
+    SLIST_FOREACH(dsc, &chr->dscs, next) {
+        if (ble_uuid_cmp(&dsc->dsc.uuid.u, dsc_uuid) == 0) {
+            return dsc;
+        }
+    }
+
+    return NULL;
+}
+
+static int
+peer_svc_add(struct peer *peer, const struct ble_gatt_svc *gatt_svc)
+{
+    struct peer_svc *prev;
+    struct peer_svc *svc;
+
+    svc = peer_svc_find(peer, gatt_svc->start_handle, &prev);
+    if (svc != NULL) {
+        /* Service already discovered. */
+        return 0;
+    }
+
+    svc = os_memblock_get(&peer_svc_pool);
+    if (svc == NULL) {
+        /* Out of memory. */
+        return BLE_HS_ENOMEM;
+    }
+    memset(svc, 0, sizeof *svc);
+
+    svc->svc = *gatt_svc;
+    SLIST_INIT(&svc->chrs);
+
+    if (prev == NULL) {
+        SLIST_INSERT_HEAD(&peer->svcs, svc, next);
+    } else {
+        SLIST_INSERT_AFTER(prev, svc, next);
+    }
+
+    return 0;
+}
+
+static void
+peer_svc_delete(struct peer_svc *svc)
+{
+    struct peer_chr *chr;
+
+    while ((chr = SLIST_FIRST(&svc->chrs)) != NULL) {
+        SLIST_REMOVE_HEAD(&svc->chrs, next);
+        peer_chr_delete(chr);
+    }
+
+    os_memblock_put(&peer_svc_pool, svc);
+}
+
+static int
+peer_svc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
+                const struct ble_gatt_svc *service, void *arg)
+{
+    struct peer *peer;
+    int rc;
+
+    peer = arg;
+    assert(peer->conn_handle == conn_handle);
+
+    switch (error->status) {
+    case 0:
+        rc = peer_svc_add(peer, service);
+        break;
+
+    case BLE_HS_EDONE:
+        /* All services discovered; start discovering characteristics. */
+        if (peer->disc_prev_chr_val > 0) {
+            peer_disc_chrs(peer);
+        }
+        rc = 0;
+        break;
+
+    default:
+        rc = error->status;
+        break;
+    }
+
+    if (rc != 0) {
+        /* Error; abort discovery. */
+        peer_disc_complete(peer, rc);
+    }
+
+    return rc;
+}
+
+int
+peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb, void *disc_cb_arg)
+{
+    struct peer_svc *svc;
+    struct peer *peer;
+    int rc;
+
+    peer = peer_find(conn_handle);
+    if (peer == NULL) {
+        return BLE_HS_ENOTCONN;
+    }
+
+    /* Undiscover everything first. */
+    while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) {
+        SLIST_REMOVE_HEAD(&peer->svcs, next);
+        peer_svc_delete(svc);
+    }
+
+    peer->disc_prev_chr_val = 1;
+    peer->disc_cb = disc_cb;
+    peer->disc_cb_arg = disc_cb_arg;
+
+    rc = ble_gattc_disc_all_svcs(conn_handle, peer_svc_disced, peer);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+peer_delete(uint16_t conn_handle)
+{
+    struct peer_svc *svc;
+    struct peer *peer;
+    int rc;
+
+    peer = peer_find(conn_handle);
+    if (peer == NULL) {
+        return BLE_HS_ENOTCONN;
+    }
+
+    SLIST_REMOVE(&peers, peer, peer, next);
+
+    while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) {
+        SLIST_REMOVE_HEAD(&peer->svcs, next);
+        peer_svc_delete(svc);
+    }
+
+    rc = os_memblock_put(&peer_pool, peer);
+    if (rc != 0) {
+        return BLE_HS_EOS;
+    }
+
+    return 0;
+}
+
+int
+peer_add(uint16_t conn_handle)
+{
+    struct peer *peer;
+
+    /* Make sure the connection handle is unique. */
+    peer = peer_find(conn_handle);
+    if (peer != NULL) {
+        return BLE_HS_EALREADY;
+    }
+
+    peer = os_memblock_get(&peer_pool);
+    if (peer == NULL) {
+        /* Out of memory. */
+        return BLE_HS_ENOMEM;
+    }
+
+    memset(peer, 0, sizeof *peer);
+    peer->conn_handle = conn_handle;
+
+    SLIST_INSERT_HEAD(&peers, peer, next);
+
+    return 0;
+}
+
+int
+peer_count(void)
+{
+    return peer_pool.mp_num_blocks - peer_pool.mp_num_free;
+}
+
+static void
+peer_free_mem(void)
+{
+    free(peer_mem);
+    peer_mem = NULL;
+
+    free(peer_svc_mem);
+    peer_svc_mem = NULL;
+
+    free(peer_chr_mem);
+    peer_chr_mem = NULL;
+
+    free(peer_dsc_mem);
+    peer_dsc_mem = NULL;
+}
+
+int
+peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs)
+{
+    int rc;
+
+    /* Free memory first in case this function gets called more than once. */
+    peer_free_mem();
+
+    peer_mem = malloc(
+        OS_MEMPOOL_BYTES(max_peers, sizeof (struct peer)));
+    if (peer_mem == NULL) {
+        rc = BLE_HS_ENOMEM;
+        goto err;
+    }
+
+    rc = os_mempool_init(&peer_pool, max_peers,
+                         sizeof (struct peer), peer_mem,
+                         "peer_pool");
+    if (rc != 0) {
+        rc = BLE_HS_EOS;
+        goto err;
+    }
+
+    peer_svc_mem = malloc(
+        OS_MEMPOOL_BYTES(max_svcs, sizeof (struct peer_svc)));
+    if (peer_svc_mem == NULL) {
+        rc = BLE_HS_ENOMEM;
+        goto err;
+    }
+
+    rc = os_mempool_init(&peer_svc_pool, max_svcs,
+                         sizeof (struct peer_svc), peer_svc_mem,
+                         "peer_svc_pool");
+    if (rc != 0) {
+        rc = BLE_HS_EOS;
+        goto err;
+    }
+
+    peer_chr_mem = malloc(
+        OS_MEMPOOL_BYTES(max_chrs, sizeof (struct peer_chr)));
+    if (peer_chr_mem == NULL) {
+        rc = BLE_HS_ENOMEM;
+        goto err;
+    }
+
+    rc = os_mempool_init(&peer_chr_pool, max_chrs,
+                         sizeof (struct peer_chr), peer_chr_mem,
+                         "peer_chr_pool");
+    if (rc != 0) {
+        rc = BLE_HS_EOS;
+        goto err;
+    }
+
+    peer_dsc_mem = malloc(
+        OS_MEMPOOL_BYTES(max_dscs, sizeof (struct peer_dsc)));
+    if (peer_dsc_mem == NULL) {
+        rc = BLE_HS_ENOMEM;
+        goto err;
+    }
+
+    rc = os_mempool_init(&peer_dsc_pool, max_dscs,
+                         sizeof (struct peer_dsc), peer_dsc_mem,
+                         "peer_dsc_pool");
+    if (rc != 0) {
+        rc = BLE_HS_EOS;
+        goto err;
+    }
+
+    return 0;
+
+err:
+    peer_free_mem();
+    return rc;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsncent/syscfg.yml
----------------------------------------------------------------------
diff --git a/apps/bsncent/syscfg.yml b/apps/bsncent/syscfg.yml
new file mode 100644
index 0000000..dc8633d
--- /dev/null
+++ b/apps/bsncent/syscfg.yml
@@ -0,0 +1,26 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.defs:
+    BSNCENT_BLE_NAME:
+        description: The BLE name to use.
+        value: '"bsncent"'
+
+syscfg.vals:
+    # Disable logging.
+    LOG_LEVEL: 255

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/pkg.yml
----------------------------------------------------------------------
diff --git a/apps/bsnprph/pkg.yml b/apps/bsnprph/pkg.yml
new file mode 100644
index 0000000..2328798
--- /dev/null
+++ b/apps/bsnprph/pkg.yml
@@ -0,0 +1,42 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+pkg.name: apps/bsnprph
+pkg.type: app
+pkg.description: Simple BLE peripheral application.
+pkg.author: "Apache Mynewt <de...@mynewt.incubator.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+    - boot/split
+    - kernel/os
+    - mgmt/imgmgr
+    - mgmt/newtmgr
+    - mgmt/newtmgr/transport/ble
+    - net/nimble/controller
+    - net/nimble/host
+    - net/nimble/host/services/ans
+    - net/nimble/host/services/gap
+    - net/nimble/host/services/gatt
+    - net/nimble/host/store/ram
+    - net/nimble/transport/ram
+    - sys/console/full
+    - sys/log/full
+    - sys/stats/full
+    - sys/sysinit
+    - sys/id

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/src/bleprph.h
----------------------------------------------------------------------
diff --git a/apps/bsnprph/src/bleprph.h b/apps/bsnprph/src/bleprph.h
new file mode 100644
index 0000000..afcbb72
--- /dev/null
+++ b/apps/bsnprph/src/bleprph.h
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_BLEPRPH_
+#define H_BLEPRPH_
+
+#include "log/log.h"
+#include "nimble/ble.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ble_hs_cfg;
+struct ble_gatt_register_ctxt;
+
+extern struct log bleprph_log;
+
+/* bleprph uses the first "peruser" log module. */
+#define BLEPRPH_LOG_MODULE  (LOG_MODULE_PERUSER + 0)
+
+/* Convenience macro for logging to the bleprph module. */
+#define BLEPRPH_LOG(lvl, ...) \
+    LOG_ ## lvl(&bleprph_log, BLEPRPH_LOG_MODULE, __VA_ARGS__)
+
+/** GATT server. */
+#define GATT_SVR_SVC_ALERT_UUID               0x1811
+#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID   0x2A47
+#define GATT_SVR_CHR_NEW_ALERT                0x2A46
+#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID   0x2A48
+#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID      0x2A45
+#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT        0x2A44
+
+void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
+int gatt_svr_init(void);
+
+/** Misc. */
+void print_bytes(const uint8_t *bytes, int len);
+void print_addr(const void *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/src/bsnprph.h
----------------------------------------------------------------------
diff --git a/apps/bsnprph/src/bsnprph.h b/apps/bsnprph/src/bsnprph.h
new file mode 100644
index 0000000..d8e814f
--- /dev/null
+++ b/apps/bsnprph/src/bsnprph.h
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_BSNPRPH_
+#define H_BSNPRPH_
+
+#include "log/log.h"
+#include "nimble/ble.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ble_hs_cfg;
+struct ble_gatt_register_ctxt;
+
+extern struct log bsnprph_log;
+
+/* bsnprph uses the first "peruser" log module. */
+#define BSNPRPH_LOG_MODULE  (LOG_MODULE_PERUSER + 0)
+
+/* Convenience macro for logging to the bsnprph module. */
+#define BSNPRPH_LOG(lvl, ...) \
+    LOG_ ## lvl(&bsnprph_log, BSNPRPH_LOG_MODULE, __VA_ARGS__)
+
+/** GATT server. */
+#define GATT_SVR_SVC_ALERT_UUID               0x1811
+#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID   0x2A47
+#define GATT_SVR_CHR_NEW_ALERT                0x2A46
+#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID   0x2A48
+#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID      0x2A45
+#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT        0x2A44
+
+void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
+int gatt_svr_init(void);
+
+/** Misc. */
+void print_bytes(const uint8_t *bytes, int len);
+void print_addr(const void *addr);
+
+extern uint16_t gatt_svr_chr_gendata_val_handle;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/src/gatt_svr.c
----------------------------------------------------------------------
diff --git a/apps/bsnprph/src/gatt_svr.c b/apps/bsnprph/src/gatt_svr.c
new file mode 100644
index 0000000..f8c2af9
--- /dev/null
+++ b/apps/bsnprph/src/gatt_svr.c
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include "bsp/bsp.h"
+#include "host/ble_hs.h"
+#include "host/ble_uuid.h"
+#include "bsnprph.h"
+
+/**
+ * The vendor specific security test service consists of two characteristics:
+ *     o random-number-generator: generates a random 32-bit number each time
+ *       it is read.  This characteristic can only be read over an encrypted
+ *       connection.
+ *     o static-value: a single-byte characteristic that can always be read,
+ *       but can only be written over an encrypted connection.
+ */
+
+/* c66f3301-33b3-4687-850a-d52b0d5d1e3c */
+static const ble_uuid128_t gatt_svr_svc_gendata_uuid =
+    BLE_UUID128_INIT(0x3c, 0x1e, 0x5d, 0x0d, 0x2b, 0xd5, 0x0a, 0x85,
+                     0x87, 0x46, 0xb3, 0x33, 0x01, 0x33, 0x6f, 0xc6);
+
+/* c66f3301-33b3-4687-850a-d52b0d5d1e3d */
+static const ble_uuid128_t gatt_svr_chr_gendata_uuid =
+    BLE_UUID128_INIT(0x3d, 0x1e, 0x5d, 0x0d, 0x2b, 0xd5, 0x0a, 0x85,
+                     0x87, 0x46, 0xb3, 0x33, 0x01, 0x33, 0x6f, 0xc6);
+
+static int
+gatt_svr_chr_access_gendata(uint16_t conn_handle, uint16_t attr_handle,
+                            struct ble_gatt_access_ctxt *ctxt,
+                            void *arg);
+
+uint16_t gatt_svr_chr_gendata_val_handle;
+
+static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
+    {
+        /*** Service: Generic data. */
+        .type = BLE_GATT_SVC_TYPE_PRIMARY,
+        .uuid = &gatt_svr_svc_gendata_uuid.u,
+        .characteristics = (struct ble_gatt_chr_def[]) { {
+            /*** Characteristic: Generic data. */
+            .uuid = &gatt_svr_chr_gendata_uuid.u,
+            .access_cb = gatt_svr_chr_access_gendata,
+            .flags = BLE_GATT_CHR_F_NOTIFY,
+            .val_handle = &gatt_svr_chr_gendata_val_handle,
+        }, {
+            0, /* No more characteristics in this service. */
+        } },
+    },
+
+    {
+        0, /* No more services. */
+    },
+};
+
+static int
+gatt_svr_chr_access_gendata(uint16_t conn_handle, uint16_t attr_handle,
+                            struct ble_gatt_access_ctxt *ctxt,
+                            void *arg)
+{
+    return BLE_ATT_ERR_UNLIKELY;
+}
+
+void
+gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
+{
+    char buf[BLE_UUID_STR_LEN];
+
+    switch (ctxt->op) {
+    case BLE_GATT_REGISTER_OP_SVC:
+        BSNPRPH_LOG(DEBUG, "registered service %s with handle=%d\n",
+                    ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
+                    ctxt->svc.handle);
+        break;
+
+    case BLE_GATT_REGISTER_OP_CHR:
+        BSNPRPH_LOG(DEBUG, "registering characteristic %s with "
+                           "def_handle=%d val_handle=%d\n",
+                    ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
+                    ctxt->chr.def_handle,
+                    ctxt->chr.val_handle);
+        break;
+
+    case BLE_GATT_REGISTER_OP_DSC:
+        BSNPRPH_LOG(DEBUG, "registering descriptor %s with handle=%d\n",
+                    ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
+                    ctxt->dsc.handle);
+        break;
+
+    default:
+        assert(0);
+        break;
+    }
+}
+
+int
+gatt_svr_init(void)
+{
+    int rc;
+
+    rc = ble_gatts_count_cfg(gatt_svr_svcs);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = ble_gatts_add_svcs(gatt_svr_svcs);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/src/main.c
----------------------------------------------------------------------
diff --git a/apps/bsnprph/src/main.c b/apps/bsnprph/src/main.c
new file mode 100755
index 0000000..23e8b5a
--- /dev/null
+++ b/apps/bsnprph/src/main.c
@@ -0,0 +1,378 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include "sysinit/sysinit.h"
+#include "bsp/bsp.h"
+#include "os/os.h"
+#include "bsp/bsp.h"
+#include "hal/hal_gpio.h"
+#include "console/console.h"
+#include "hal/hal_system.h"
+#include "config/config.h"
+#include "split/split.h"
+
+/* BLE */
+#include "nimble/ble.h"
+#include "host/ble_hs.h"
+#include "services/gap/ble_svc_gap.h"
+
+/* Application-specified header. */
+#include "bsnprph.h"
+
+#define BSNPRPH_PKT_SZ          80
+#define BSNPRPH_TX_TIMER_RATE   2
+
+static const uint8_t bsnprph_prph_public_addr[6] = {
+    0x0a, 0x0b, 0x09, 0x09, 0x09, 0x05,
+};
+
+static const ble_addr_t bsnprph_central_addr = {
+    BLE_ADDR_PUBLIC,
+    { 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x00 },
+};
+
+struct log bsnprph_log;
+
+/* Sends data to central at 60 Hz. */
+static struct os_callout bsnprph_tx_timer;
+
+/* The handle of the current connection. */
+static uint16_t bsnprph_conn_handle;
+
+static int bsnprph_gap_event(struct ble_gap_event *event, void *arg);
+
+/**
+ * Logs information about a connection to the console.
+ */
+static void
+bsnprph_print_conn_desc(struct ble_gap_conn_desc *desc)
+{
+    BSNPRPH_LOG(INFO, "handle=%d our_ota_addr_type=%d our_ota_addr=",
+                desc->conn_handle, desc->our_ota_addr.type);
+    print_addr(desc->our_ota_addr.val);
+    BSNPRPH_LOG(INFO, " our_id_addr_type=%d our_id_addr=",
+                desc->our_id_addr.type);
+    print_addr(desc->our_id_addr.val);
+    BSNPRPH_LOG(INFO, " peer_ota_addr_type=%d peer_ota_addr=",
+                desc->peer_ota_addr.type);
+    print_addr(desc->peer_ota_addr.val);
+    BSNPRPH_LOG(INFO, " peer_id_addr_type=%d peer_id_addr=",
+                desc->peer_id_addr.type);
+    print_addr(desc->peer_id_addr.val);
+    BSNPRPH_LOG(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d "
+                "encrypted=%d authenticated=%d bonded=%d\n",
+                desc->conn_itvl, desc->conn_latency,
+                desc->supervision_timeout,
+                desc->sec_state.encrypted,
+                desc->sec_state.authenticated,
+                desc->sec_state.bonded);
+}
+
+/**
+ * Enables advertising with the following parameters:
+ *     o General discoverable mode.
+ *     o Directed connectable mode.
+ */
+static void
+bsnprph_advertise(void)
+{
+    struct ble_gap_adv_params adv_params;
+    struct ble_hs_adv_fields fields;
+    const char *name;
+    int rc;
+
+    /**
+     *  Set the advertisement data included in our advertisements:
+     *     o Flags (indicates advertisement type and other general info).
+     *     o Advertising tx power.
+     *     o Device name.
+     *     o 16-bit service UUIDs (alert notifications).
+     */
+
+    memset(&fields, 0, sizeof fields);
+
+    /* Advertise two flags:
+     *     o Discoverability in forthcoming advertisement (general)
+     *     o BLE-only (BR/EDR unsupported).
+     */
+    fields.flags = BLE_HS_ADV_F_DISC_GEN |
+                   BLE_HS_ADV_F_BREDR_UNSUP;
+
+    /* Indicate that the TX power level field should be included; have the
+     * stack fill this value automatically.  This is done by assiging the
+     * special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
+     */
+    fields.tx_pwr_lvl_is_present = 1;
+    fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
+
+    name = ble_svc_gap_device_name();
+    fields.name = (uint8_t *)name;
+    fields.name_len = strlen(name);
+    fields.name_is_complete = 1;
+
+    fields.uuids16 = (ble_uuid16_t[]){
+        BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID)
+    };
+    fields.num_uuids16 = 1;
+    fields.uuids16_is_complete = 1;
+
+    rc = ble_gap_adv_set_fields(&fields);
+    if (rc != 0) {
+        BSNPRPH_LOG(ERROR, "error setting advertisement data; rc=%d\n", rc);
+        return;
+    }
+
+    /* Begin advertising. */
+    memset(&adv_params, 0, sizeof adv_params);
+    adv_params.conn_mode = BLE_GAP_CONN_MODE_DIR;
+    adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
+    adv_params.high_duty_cycle = 1;
+    rc = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, &bsnprph_central_addr,
+                           BLE_HS_FOREVER, &adv_params,
+                           bsnprph_gap_event, NULL);
+    if (rc != 0) {
+        BSNPRPH_LOG(ERROR, "error enabling advertisement; rc=%d\n", rc);
+        return;
+    }
+}
+
+static void
+bsnprph_tx_timer_reset(void)
+{
+    int rc;
+
+    rc = os_callout_reset(&bsnprph_tx_timer, BSNPRPH_TX_TIMER_RATE);
+    assert(rc == 0);
+}
+
+/**
+ * Transmits dummy data at 60 Hz.
+ */
+static void
+bsnprph_tx_timer_exp(struct os_event *ev)
+{
+    static uint8_t buf[BSNPRPH_PKT_SZ];
+    static uint8_t val;
+
+    struct os_mbuf *om;
+    int rc;
+
+    memset(buf, val, sizeof buf);
+    val++;
+
+    om = ble_hs_mbuf_from_flat(buf, sizeof buf);
+    if (om == NULL) {
+        /* XXX: OOM; log this. */
+    } else {
+        rc = ble_gattc_notify_custom(bsnprph_conn_handle,
+                                     gatt_svr_chr_gendata_val_handle, om);
+        if (rc != 0) {
+            /* XXX: Log this. */
+        }
+    }
+
+    bsnprph_tx_timer_reset();
+}
+
+/**
+ * The nimble host executes this callback when a GAP event occurs.  The
+ * application associates a GAP event callback with each connection that forms.
+ * bsnprph uses the same callback for all connections.
+ *
+ * @param event                 The type of event being signalled.
+ * @param ctxt                  Various information pertaining to the event.
+ * @param arg                   Application-specified argument; unuesd by
+ *                                  bsnprph.
+ *
+ * @return                      0 if the application successfully handled the
+ *                                  event; nonzero on failure.  The semantics
+ *                                  of the return code is specific to the
+ *                                  particular GAP event being signalled.
+ */
+static int
+bsnprph_gap_event(struct ble_gap_event *event, void *arg)
+{
+    struct ble_gap_conn_desc desc;
+    int rc;
+
+    switch (event->type) {
+    case BLE_GAP_EVENT_CONNECT:
+        /* A new connection was established or a connection attempt failed. */
+        BSNPRPH_LOG(INFO, "connection %s; status=%d ",
+                       event->connect.status == 0 ? "established" : "failed",
+                       event->connect.status);
+        if (event->connect.status == 0) {
+            rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+            assert(rc == 0);
+            bsnprph_print_conn_desc(&desc);
+
+            bsnprph_conn_handle = event->connect.conn_handle;
+        }
+        BSNPRPH_LOG(INFO, "\n");
+
+        if (event->connect.status != 0) {
+            /* Connection failed; resume advertising. */
+            bsnprph_advertise();
+        }
+        return 0;
+
+    case BLE_GAP_EVENT_DISCONNECT:
+        os_callout_stop(&bsnprph_tx_timer);
+
+        BSNPRPH_LOG(INFO, "disconnect; reason=%d ", event->disconnect.reason);
+        bsnprph_print_conn_desc(&event->disconnect.conn);
+        BSNPRPH_LOG(INFO, "\n");
+
+        /* Connection terminated; resume advertising. */
+        bsnprph_advertise();
+        return 0;
+
+    case BLE_GAP_EVENT_ADV_COMPLETE:
+        os_callout_stop(&bsnprph_tx_timer);
+        BSNPRPH_LOG(INFO, "adv complete\n");
+        bsnprph_advertise();
+        return 0;
+
+    case BLE_GAP_EVENT_CONN_UPDATE:
+        /* The central has updated the connection parameters. */
+        BSNPRPH_LOG(INFO, "connection updated; status=%d ",
+                    event->conn_update.status);
+        rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+        assert(rc == 0);
+        bsnprph_print_conn_desc(&desc);
+        BSNPRPH_LOG(INFO, "\n");
+        return 0;
+
+    case BLE_GAP_EVENT_ENC_CHANGE:
+        /* Encryption has been enabled or disabled for this connection. */
+        BSNPRPH_LOG(INFO, "encryption change event; status=%d ",
+                    event->enc_change.status);
+        rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+        assert(rc == 0);
+        bsnprph_print_conn_desc(&desc);
+        BSNPRPH_LOG(INFO, "\n");
+        return 0;
+
+    case BLE_GAP_EVENT_SUBSCRIBE:
+        BSNPRPH_LOG(INFO, "subscribe event; conn_handle=%d attr_handle=%d "
+                          "reason=%d prevn=%d curn=%d previ=%d curi=%d\n",
+                    event->subscribe.conn_handle,
+                    event->subscribe.attr_handle,
+                    event->subscribe.reason,
+                    event->subscribe.prev_notify,
+                    event->subscribe.cur_notify,
+                    event->subscribe.prev_indicate,
+                    event->subscribe.cur_indicate);
+        if (event->subscribe.attr_handle == gatt_svr_chr_gendata_val_handle) {
+            /* Start transmitting notifications. */
+            bsnprph_tx_timer_reset();
+        }
+        return 0;
+
+    case BLE_GAP_EVENT_MTU:
+        BSNPRPH_LOG(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
+                    event->mtu.conn_handle,
+                    event->mtu.channel_id,
+                    event->mtu.value);
+        return 0;
+    }
+
+    return 0;
+}
+
+static void
+bsnprph_on_reset(int reason)
+{
+    BSNPRPH_LOG(ERROR, "Resetting state; reason=%d\n", reason);
+}
+
+static void
+bsnprph_on_sync(void)
+{
+    /* Begin advertising. */
+    bsnprph_advertise();
+}
+
+/**
+ * main
+ *
+ * The main task for the project. This function initializes the packages,
+ * then starts serving events from default event queue.
+ *
+ * @return int NOTE: this function should never return!
+ */
+int
+main(void)
+{
+    int rc;
+
+    /* Initialize OS */
+    sysinit();
+
+    /* Set initial BLE device address. */
+    memcpy(g_dev_addr, bsnprph_prph_public_addr, 6);
+
+    /* Initialize the bsnprph log. */
+    log_register("bsnprph", &bsnprph_log, &log_console_handler, NULL,
+                 LOG_SYSLEVEL);
+
+    /* Initialize the NimBLE host configuration. */
+    log_register("ble_hs", &ble_hs_log, &log_console_handler, NULL,
+                 LOG_SYSLEVEL);
+    ble_hs_cfg.reset_cb = bsnprph_on_reset;
+    ble_hs_cfg.sync_cb = bsnprph_on_sync;
+    ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
+
+    os_callout_init(&bsnprph_tx_timer, os_eventq_dflt_get(),
+                    bsnprph_tx_timer_exp, NULL);
+
+    rc = gatt_svr_init();
+    assert(rc == 0);
+
+    /* Set the default device name. */
+    rc = ble_svc_gap_device_name_set(MYNEWT_VAL(BSNPRPH_BLE_NAME));
+    assert(rc == 0);
+
+    conf_load();
+
+    /* If this app is acting as the loader in a split image setup, jump into
+     * the second stage application instead of starting the OS.
+     */
+#if MYNEWT_VAL(SPLIT_LOADER)
+    {
+        void *entry;
+        rc = split_app_go(&entry, true);
+        if (rc == 0) {
+            hal_system_start(entry);
+        }
+    }
+#endif
+
+    /*
+     * As the last thing, process events from default event queue.
+     */
+    while (1) {
+        os_eventq_run(os_eventq_dflt_get());
+    }
+    return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/src/misc.c
----------------------------------------------------------------------
diff --git a/apps/bsnprph/src/misc.c b/apps/bsnprph/src/misc.c
new file mode 100644
index 0000000..72dc813
--- /dev/null
+++ b/apps/bsnprph/src/misc.c
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "bsnprph.h"
+
+/**
+ * Utility function to log an array of bytes.
+ */
+void
+print_bytes(const uint8_t *bytes, int len)
+{
+    int i;
+
+    for (i = 0; i < len; i++) {
+        BSNPRPH_LOG(INFO, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
+    }
+}
+
+void
+print_addr(const void *addr)
+{
+    const uint8_t *u8p;
+
+    u8p = addr;
+    BSNPRPH_LOG(INFO, "%02x:%02x:%02x:%02x:%02x:%02x",
+                u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/syscfg.yml
----------------------------------------------------------------------
diff --git a/apps/bsnprph/syscfg.yml b/apps/bsnprph/syscfg.yml
new file mode 100644
index 0000000..45082c2
--- /dev/null
+++ b/apps/bsnprph/syscfg.yml
@@ -0,0 +1,35 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.defs:
+    BSNPRPH_BLE_NAME:
+        description: The BLE name to use.
+        value: '"bsnprph"'
+
+syscfg.vals:
+    # Use INFO log level to reduce code size.  DEBUG is too large for nRF51.
+    LOG_LEVEL: 1
+
+    # Disable central and observer roles.
+    BLE_ROLE_BROADCASTER: 1
+    BLE_ROLE_CENTRAL: 0
+    BLE_ROLE_OBSERVER: 0
+    BLE_ROLE_PERIPHERAL: 1
+
+    # Disable unused eddystone feature.
+    BLE_EDDYSTONE: 0

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/net/nimble/controller/include/controller/ble_ll_conn.h
----------------------------------------------------------------------
diff --git a/net/nimble/controller/include/controller/ble_ll_conn.h b/net/nimble/controller/include/controller/ble_ll_conn.h
index d0badff..dd45b16 100644
--- a/net/nimble/controller/include/controller/ble_ll_conn.h
+++ b/net/nimble/controller/include/controller/ble_ll_conn.h
@@ -154,6 +154,10 @@ struct ble_ll_conn_sm
     uint8_t last_unmapped_chan;
     uint8_t num_used_chans;
 
+#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
+    uint8_t period_occ_mask;    /* mask: period 0 = 0x01, period 3 = 0x08 */
+#endif
+
     /* RSSI */
     int8_t conn_rssi;
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/net/nimble/controller/include/controller/ble_ll_sched.h
----------------------------------------------------------------------
diff --git a/net/nimble/controller/include/controller/ble_ll_sched.h b/net/nimble/controller/include/controller/ble_ll_sched.h
index 3a05cfe..2872730 100644
--- a/net/nimble/controller/include/controller/ble_ll_sched.h
+++ b/net/nimble/controller/include/controller/ble_ll_sched.h
@@ -69,6 +69,40 @@ extern "C" {
 struct ble_ll_sched_item;
 typedef int (*sched_cb_func)(struct ble_ll_sched_item *sch);
 
+/*
+ * Strict connection scheduling (for the master) is different than how
+ * connections are normally scheduled. With strict connection scheduling we
+ * introduce the concept of a "period". A period is a collection of slots. Each
+ * slot is 1.25 msecs in length. The number of slots in a period is determined
+ * by the syscfg value BLE_LL_CONN_INIT_SLOTS. A collection of periods is called
+ * an epoch. The length of an epoch is determined by the number of connections
+ * (BLE_MAX_CONNECTIONS plus BLE_LL_ADD_STRICT_SCHED_PERIODS). Connections
+ * will be scheduled at period boundaries. Any scanning/initiating/advertising
+ * will be done in unused periods, if possible.
+ */
+#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
+#define BLE_LL_SCHED_PERIODS    (MYNEWT_VAL(BLE_MAX_CONNECTIONS) + \
+                                 MYNEWT_VAL(BLE_LL_ADD_STRICT_SCHED_PERIODS))
+
+struct ble_ll_sched_obj
+{
+    uint8_t sch_num_occ_periods;
+    uint32_t sch_occ_period_mask;
+    uint32_t sch_ticks_per_period;
+    uint32_t sch_ticks_per_epoch;
+    uint32_t sch_epoch_start;
+};
+
+extern struct ble_ll_sched_obj g_ble_ll_sched_data;
+
+/*
+ * XXX: TODO:
+ * -> How do we know epoch start is up to date? Not wrapped?
+ * -> for now, only do this with no more than 32 connections.
+ * -> Do not let initiating occur if no empty sched slots
+ */
+#endif
+
 struct ble_ll_sched_item
 {
     uint8_t         sched_type;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/net/nimble/controller/src/ble_ll.c
----------------------------------------------------------------------
diff --git a/net/nimble/controller/src/ble_ll.c b/net/nimble/controller/src/ble_ll.c
index 6071d3c..5db6ba2 100644
--- a/net/nimble/controller/src/ble_ll.c
+++ b/net/nimble/controller/src/ble_ll.c
@@ -1164,6 +1164,9 @@ ble_ll_reset(void)
     /* All this does is re-initialize the event masks so call the hci init */
     ble_ll_hci_init();
 
+    /* Reset scheduler */
+    ble_ll_sched_init();
+
     /* Set state to standby */
     ble_ll_state_set(BLE_LL_STATE_STANDBY);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/net/nimble/controller/src/ble_ll_conn.c
----------------------------------------------------------------------
diff --git a/net/nimble/controller/src/ble_ll_conn.c b/net/nimble/controller/src/ble_ll_conn.c
index d8d3f8f..7a61d74 100644
--- a/net/nimble/controller/src/ble_ll_conn.c
+++ b/net/nimble/controller/src/ble_ll_conn.c
@@ -698,6 +698,11 @@ ble_ll_conn_continue_rx_encrypt(void *arg)
 static uint32_t
 ble_ll_conn_get_next_sched_time(struct ble_ll_conn_sm *connsm)
 {
+
+#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
+    uint32_t ce_end;
+    ce_end = connsm->ce_end_time;
+#else
     uint32_t itvl;
     uint32_t ce_end;
     uint32_t next_sched_time;
@@ -711,6 +716,7 @@ ble_ll_conn_get_next_sched_time(struct ble_ll_conn_sm *connsm)
             ce_end = next_sched_time;
         }
     }
+#endif
 
     return ce_end;
 }
@@ -1552,6 +1558,9 @@ ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err)
     uint8_t *evbuf;
     struct os_mbuf *m;
     struct os_mbuf_pkthdr *pkthdr;
+#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
+    os_sr_t sr;
+#endif
 
     /* Remove scheduler events just in case */
     ble_ll_sched_rmv_elem(&connsm->conn_sch);
@@ -1588,6 +1597,16 @@ ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err)
     /* Make sure events off queue */
     os_eventq_remove(&g_ble_ll_data.ll_evq, &connsm->conn_ev_end);
 
+#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
+    /* Remove from occupied periods */
+    OS_ENTER_CRITICAL(sr);
+    assert(g_ble_ll_sched_data.sch_num_occ_periods > 0);
+    assert(g_ble_ll_sched_data.sch_occ_period_mask & connsm->period_occ_mask);
+    --g_ble_ll_sched_data.sch_num_occ_periods;
+    g_ble_ll_sched_data.sch_occ_period_mask &= ~connsm->period_occ_mask;
+    OS_EXIT_CRITICAL(sr);
+#endif
+
     /* Connection state machine is now idle */
     connsm->conn_state = BLE_LL_CONN_STATE_IDLE;
 
@@ -1611,7 +1630,8 @@ ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err)
     STAILQ_INSERT_TAIL(&g_ble_ll_conn_free_list, connsm, free_stqe);
 
     /* Log connection end */
-    ble_ll_log(BLE_LL_LOG_ID_CONN_END,connsm->conn_handle,0,connsm->event_cntr);
+    ble_ll_log(BLE_LL_LOG_ID_CONN_END,connsm->conn_handle, ble_err,
+               connsm->event_cntr);
 }
 
 /**
@@ -1736,7 +1756,11 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm)
      * Calculate ce end time. For a slave, we need to add window widening and
      * the transmit window if we still have one.
      */
+#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_USECS_PER_SLOT;
+#endif
     if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
         cur_ww = ble_ll_conn_calc_window_widening(connsm);
         max_ww = (connsm->conn_itvl * (BLE_LL_CONN_ITVL_USECS/2)) - BLE_LL_IFS;
@@ -1804,8 +1828,13 @@ ble_ll_conn_created(struct ble_ll_conn_sm *connsm, uint32_t endtime,
             connsm->tx_win_size * BLE_LL_CONN_TX_WIN_USECS;
         usecs = 1250 + (connsm->tx_win_off * BLE_LL_CONN_TX_WIN_USECS);
         connsm->anchor_point = endtime + os_cputime_usecs_to_ticks(usecs);
+#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
+        usecs = connsm->slave_cur_tx_win_usecs +
+            g_ble_ll_sched_data.sch_ticks_per_period;
+#else
         usecs = connsm->slave_cur_tx_win_usecs +
             (MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_USECS_PER_SLOT);
+#endif
         connsm->ce_end_time = connsm->anchor_point +
             os_cputime_usecs_to_ticks(usecs);
         connsm->slave_cur_window_widening = 0;