summaryrefslogtreecommitdiff
path: root/src/libs/mynewt-nimble/apps/blecent
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/mynewt-nimble/apps/blecent')
-rw-r--r--src/libs/mynewt-nimble/apps/blecent/pkg.yml36
-rw-r--r--src/libs/mynewt-nimble/apps/blecent/src/blecent.h111
-rw-r--r--src/libs/mynewt-nimble/apps/blecent/src/main.c525
-rw-r--r--src/libs/mynewt-nimble/apps/blecent/src/misc.c209
-rw-r--r--src/libs/mynewt-nimble/apps/blecent/src/peer.c807
-rw-r--r--src/libs/mynewt-nimble/apps/blecent/syscfg.yml30
6 files changed, 1718 insertions, 0 deletions
diff --git a/src/libs/mynewt-nimble/apps/blecent/pkg.yml b/src/libs/mynewt-nimble/apps/blecent/pkg.yml
new file mode 100644
index 00000000..dd574395
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blecent/pkg.yml
@@ -0,0 +1,36 @@
+# 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/blecent
+pkg.type: app
+pkg.description: Simple BLE central application.
+pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+ - "@apache-mynewt-core/kernel/os"
+ - "@apache-mynewt-core/sys/console/full"
+ - "@apache-mynewt-core/sys/log/full"
+ - "@apache-mynewt-core/sys/log/modlog"
+ - "@apache-mynewt-core/sys/stats/full"
+ - nimble/host
+ - nimble/host/util
+ - nimble/host/services/gap
+ - nimble/host/services/gatt
+ - nimble/host/store/ram
+ - nimble/transport
diff --git a/src/libs/mynewt-nimble/apps/blecent/src/blecent.h b/src/libs/mynewt-nimble/apps/blecent/src/blecent.h
new file mode 100644
index 00000000..a694f402
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blecent/src/blecent.h
@@ -0,0 +1,111 @@
+/*
+ * 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_BLECENT_
+#define H_BLECENT_
+
+#include "os/mynewt.h"
+#include "modlog/modlog.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;
+
+#define BLECENT_SVC_ALERT_UUID 0x1811
+#define BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47
+#define BLECENT_CHR_NEW_ALERT 0x2A46
+#define BLECENT_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48
+#define BLECENT_CHR_UNR_ALERT_STAT_UUID 0x2A45
+#define BLECENT_CHR_ALERT_NOT_CTRL_PT 0x2A44
+
+/** 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_init(int max_peers, int max_svcs, int max_chrs, int max_dscs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/blecent/src/main.c b/src/libs/mynewt-nimble/apps/blecent/src/main.c
new file mode 100644
index 00000000..788f2115
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blecent/src/main.c
@@ -0,0 +1,525 @@
+/*
+ * 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 "os/mynewt.h"
+#include "bsp/bsp.h"
+
+/* BLE */
+#include "nimble/ble.h"
+#include "host/ble_hs.h"
+#include "host/util/util.h"
+
+/* Mandatory services. */
+#include "services/gap/ble_svc_gap.h"
+#include "services/gatt/ble_svc_gatt.h"
+
+/* Application-specified header. */
+#include "blecent.h"
+
+static int blecent_gap_event(struct ble_gap_event *event, void *arg);
+
+/**
+ * Application callback. Called when the read of the ANS Supported New Alert
+ * Category characteristic has completed.
+ */
+static int
+blecent_on_read(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ struct ble_gatt_attr *attr,
+ void *arg)
+{
+ MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status,
+ conn_handle);
+ if (error->status == 0) {
+ MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle);
+ print_mbuf(attr->om);
+ }
+ MODLOG_DFLT(INFO, "\n");
+
+ return 0;
+}
+
+/**
+ * Application callback. Called when the write to the ANS Alert Notification
+ * Control Point characteristic has completed.
+ */
+static int
+blecent_on_write(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ struct ble_gatt_attr *attr,
+ void *arg)
+{
+ MODLOG_DFLT(INFO,
+ "Write complete; status=%d conn_handle=%d attr_handle=%d\n",
+ error->status, conn_handle, attr->handle);
+
+ return 0;
+}
+
+/**
+ * Application callback. Called when the attempt to subscribe to notifications
+ * for the ANS Unread Alert Status characteristic has completed.
+ */
+static int
+blecent_on_subscribe(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ struct ble_gatt_attr *attr,
+ void *arg)
+{
+ MODLOG_DFLT(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
+blecent_read_write_subscribe(const struct peer *peer)
+{
+ const struct peer_chr *chr;
+ const struct peer_dsc *dsc;
+ uint8_t value[2];
+ int rc;
+
+ /* Read the supported-new-alert-category characteristic. */
+ chr = peer_chr_find_uuid(peer,
+ BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
+ BLE_UUID16_DECLARE(BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID));
+ if (chr == NULL) {
+ MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Supported New "
+ "Alert Category characteristic\n");
+ goto err;
+ }
+
+ rc = ble_gattc_read(peer->conn_handle, chr->chr.val_handle,
+ blecent_on_read, NULL);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "Error: Failed to read characteristic; rc=%d\n",
+ rc);
+ goto err;
+ }
+
+ /* Write two bytes (99, 100) to the alert-notification-control-point
+ * characteristic.
+ */
+ chr = peer_chr_find_uuid(peer,
+ BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
+ BLE_UUID16_DECLARE(BLECENT_CHR_ALERT_NOT_CTRL_PT));
+ if (chr == NULL) {
+ MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Alert "
+ "Notification Control Point characteristic\n");
+ goto err;
+ }
+
+ value[0] = 99;
+ value[1] = 100;
+ rc = ble_gattc_write_flat(peer->conn_handle, chr->chr.val_handle,
+ value, sizeof value, blecent_on_write, NULL);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "Error: Failed to write characteristic; rc=%d\n",
+ rc);
+ }
+
+ /* Subscribe to notifications for the Unread Alert Status 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,
+ BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
+ BLE_UUID16_DECLARE(BLECENT_CHR_UNR_ALERT_STAT_UUID),
+ BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16));
+ if (dsc == NULL) {
+ MODLOG_DFLT(ERROR, "Error: Peer lacks a CCCD for the Unread Alert "
+ "Status characteristic\n");
+ goto err;
+ }
+
+ value[0] = 1;
+ value[1] = 0;
+ rc = ble_gattc_write_flat(peer->conn_handle, dsc->dsc.handle,
+ value, sizeof value, blecent_on_subscribe, NULL);
+ if (rc != 0) {
+ MODLOG_DFLT(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
+blecent_on_disc_complete(const struct peer *peer, int status, void *arg)
+{
+
+ if (status != 0) {
+ /* Service discovery failed. Terminate the connection. */
+ MODLOG_DFLT(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.
+ */
+ MODLOG_DFLT(ERROR, "Service discovery complete; status=%d "
+ "conn_handle=%d\n", status, peer->conn_handle);
+
+ /* Now perform three concurrent GATT procedures against the peer: read,
+ * write, and subscribe to notifications.
+ */
+ blecent_read_write_subscribe(peer);
+}
+
+/**
+ * Initiates the GAP general discovery procedure.
+ */
+static void
+blecent_scan(void)
+{
+ uint8_t own_addr_type;
+ struct ble_gap_disc_params disc_params;
+ int rc;
+
+ /* Figure out address to use while advertising (no privacy for now) */
+ rc = ble_hs_id_infer_auto(0, &own_addr_type);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
+ return;
+ }
+
+ /* Tell the controller to filter duplicates; we don't want to process
+ * repeated advertisements from the same device.
+ */
+ disc_params.filter_duplicates = 1;
+
+ /**
+ * Perform a passive scan. I.e., don't send follow-up scan requests to
+ * each advertiser.
+ */
+ disc_params.passive = 1;
+
+ /* Use defaults for the rest of the parameters. */
+ disc_params.itvl = 0;
+ disc_params.window = 0;
+ disc_params.filter_policy = 0;
+ disc_params.limited = 0;
+
+ rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params,
+ blecent_gap_event, NULL);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n",
+ rc);
+ }
+}
+
+/**
+ * Indicates whether we should tre to connect to the sender of the specified
+ * advertisement. The function returns a positive result if the device
+ * advertises connectability and support for the Alert Notification service.
+ */
+static int
+blecent_should_connect(const struct ble_gap_disc_desc *disc)
+{
+ struct ble_hs_adv_fields fields;
+ int rc;
+ int i;
+
+ /* The device has to be advertising connectability. */
+ if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
+ disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
+
+ return 0;
+ }
+
+ rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* The device has to advertise support for the Alert Notification
+ * service (0x1811).
+ */
+ for (i = 0; i < fields.num_uuids16; i++) {
+ if (ble_uuid_u16(&fields.uuids16[i].u) == BLECENT_SVC_ALERT_UUID) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Connects to the sender of the specified advertisement of it looks
+ * interesting. A device is "interesting" if it advertises connectability and
+ * support for the Alert Notification service.
+ */
+static void
+blecent_connect_if_interesting(const struct ble_gap_disc_desc *disc)
+{
+ uint8_t own_addr_type;
+ int rc;
+
+ /* Don't do anything if we don't care about this advertiser. */
+ if (!blecent_should_connect(disc)) {
+ return;
+ }
+
+ /* Scanning must be stopped before a connection can be initiated. */
+ rc = ble_gap_disc_cancel();
+ if (rc != 0) {
+ MODLOG_DFLT(DEBUG, "Failed to cancel scan; rc=%d\n", rc);
+ return;
+ }
+
+ /* Figure out address to use for connect (no privacy for now) */
+ rc = ble_hs_id_infer_auto(0, &own_addr_type);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
+ return;
+ }
+
+ /* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
+ * timeout.
+ */
+ rc = ble_gap_connect(own_addr_type, &disc->addr, 30000, NULL,
+ blecent_gap_event, NULL);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
+ "addr=%s\n; rc=%d",
+ disc->addr.type, addr_str(disc->addr.val), rc);
+ return;
+ }
+}
+
+/**
+ * The nimble host executes this callback when a GAP event occurs. The
+ * application associates a GAP event callback with each connection that is
+ * established. blecent uses the same callback for all connections.
+ *
+ * @param event The event being signalled.
+ * @param arg Application-specified argument; unused by
+ * blecent.
+ *
+ * @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
+blecent_gap_event(struct ble_gap_event *event, void *arg)
+{
+ struct ble_gap_conn_desc desc;
+ struct ble_hs_adv_fields fields;
+ int rc;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_DISC:
+ rc = ble_hs_adv_parse_fields(&fields, event->disc.data,
+ event->disc.length_data);
+ if (rc != 0) {
+ return 0;
+ }
+
+ /* An advertisment report was received during GAP discovery. */
+ print_adv_fields(&fields);
+
+ /* Try to connect to the advertiser if it looks interesting. */
+ blecent_connect_if_interesting(&event->disc);
+ return 0;
+
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ /* Connection successfully established. */
+ MODLOG_DFLT(INFO, "Connection established ");
+
+ rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+ assert(rc == 0);
+ print_conn_desc(&desc);
+ MODLOG_DFLT(INFO, "\n");
+
+ /* Remember peer. */
+ rc = peer_add(event->connect.conn_handle);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "Failed to add peer; rc=%d\n", rc);
+ return 0;
+ }
+
+ /* Perform service discovery. */
+ rc = peer_disc_all(event->connect.conn_handle,
+ blecent_on_disc_complete, NULL);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
+ return 0;
+ }
+ } else {
+ /* Connection attempt failed; resume scanning. */
+ MODLOG_DFLT(ERROR, "Error: Connection failed; status=%d\n",
+ event->connect.status);
+ blecent_scan();
+ }
+
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ /* Connection terminated. */
+ MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
+ print_conn_desc(&event->disconnect.conn);
+ MODLOG_DFLT(INFO, "\n");
+
+ /* Forget about peer. */
+ peer_delete(event->disconnect.conn.conn_handle);
+
+ /* Resume scanning. */
+ blecent_scan();
+ return 0;
+
+ case BLE_GAP_EVENT_DISC_COMPLETE:
+ MODLOG_DFLT(INFO, "discovery complete; reason=%d\n",
+ event->disc_complete.reason);
+ return 0;
+
+ case BLE_GAP_EVENT_ENC_CHANGE:
+ /* Encryption has been enabled or disabled for this connection. */
+ MODLOG_DFLT(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. */
+ MODLOG_DFLT(INFO, "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));
+
+ /* Attribute data is contained in event->notify_rx.attr_data. */
+ return 0;
+
+ case BLE_GAP_EVENT_MTU:
+ MODLOG_DFLT(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;
+
+ case BLE_GAP_EVENT_REPEAT_PAIRING:
+ /* We already have a bond with the peer, but it is attempting to
+ * establish a new secure link. This app sacrifices security for
+ * convenience: just throw away the old bond and accept the new link.
+ */
+
+ /* Delete the old bond. */
+ rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
+ assert(rc == 0);
+ ble_store_util_delete_peer(&desc.peer_id_addr);
+
+ /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
+ * continue with the pairing operation.
+ */
+ return BLE_GAP_REPEAT_PAIRING_RETRY;
+
+ default:
+ return 0;
+ }
+}
+
+static void
+blecent_on_reset(int reason)
+{
+ MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
+}
+
+static void
+blecent_on_sync(void)
+{
+ int rc;
+
+ /* Make sure we have proper identity address set (public preferred) */
+ rc = ble_hs_util_ensure_addr(0);
+ assert(rc == 0);
+
+ /* Begin scanning for a peripheral to connect to. */
+ blecent_scan();
+}
+
+/**
+ * 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;
+
+ /* Initialize OS */
+ sysinit();
+
+ /* Configure the host. */
+ ble_hs_cfg.reset_cb = blecent_on_reset;
+ ble_hs_cfg.sync_cb = blecent_on_sync;
+ ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
+
+ /* Initialize data structures to track connected peers. */
+ rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64);
+ assert(rc == 0);
+
+ /* Set the default device name. */
+ rc = ble_svc_gap_device_name_set("nimble-blecent");
+ 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;
+}
diff --git a/src/libs/mynewt-nimble/apps/blecent/src/misc.c b/src/libs/mynewt-nimble/apps/blecent/src/misc.c
new file mode 100644
index 00000000..6813a122
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blecent/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 "blecent.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++) {
+ MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
+ }
+}
+
+void
+print_mbuf(const struct os_mbuf *om)
+{
+ int colon;
+
+ colon = 0;
+ while (om != NULL) {
+ if (colon) {
+ MODLOG_DFLT(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];
+
+ MODLOG_DFLT(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)
+{
+ MODLOG_DFLT(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));
+ MODLOG_DFLT(DEBUG, "our_id_addr_type=%d our_id_addr=%s ",
+ desc->our_id_addr.type, addr_str(desc->our_id_addr.val));
+ MODLOG_DFLT(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ",
+ desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val));
+ MODLOG_DFLT(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ",
+ desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val));
+ MODLOG_DFLT(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) {
+ MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags);
+ }
+
+ if (fields->uuids16 != NULL) {
+ MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=",
+ fields->uuids16_is_complete ? "" : "in");
+ for (i = 0; i < fields->num_uuids16; i++) {
+ print_uuid(&fields->uuids16[i].u);
+ MODLOG_DFLT(DEBUG, " ");
+ }
+ MODLOG_DFLT(DEBUG, "\n");
+ }
+
+ if (fields->uuids32 != NULL) {
+ MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=",
+ fields->uuids32_is_complete ? "" : "in");
+ for (i = 0; i < fields->num_uuids32; i++) {
+ print_uuid(&fields->uuids32[i].u);
+ MODLOG_DFLT(DEBUG, " ");
+ }
+ MODLOG_DFLT(DEBUG, "\n");
+ }
+
+ if (fields->uuids128 != NULL) {
+ MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=",
+ fields->uuids128_is_complete ? "" : "in");
+ for (i = 0; i < fields->num_uuids128; i++) {
+ print_uuid(&fields->uuids128[i].u);
+ MODLOG_DFLT(DEBUG, " ");
+ }
+ MODLOG_DFLT(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';
+ MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n",
+ fields->name_is_complete ? "" : "in", s);
+ }
+
+ if (fields->tx_pwr_lvl_is_present) {
+ MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl);
+ }
+
+ if (fields->slave_itvl_range != NULL) {
+ MODLOG_DFLT(DEBUG, " slave_itvl_range=");
+ print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
+ MODLOG_DFLT(DEBUG, "\n");
+ }
+
+ if (fields->svc_data_uuid16 != NULL) {
+ MODLOG_DFLT(DEBUG, " svc_data_uuid16=");
+ print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len);
+ MODLOG_DFLT(DEBUG, "\n");
+ }
+
+ if (fields->public_tgt_addr != NULL) {
+ MODLOG_DFLT(DEBUG, " public_tgt_addr=");
+ u8p = fields->public_tgt_addr;
+ for (i = 0; i < fields->num_public_tgt_addrs; i++) {
+ MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p));
+ u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
+ }
+ MODLOG_DFLT(DEBUG, "\n");
+ }
+
+ if (fields->appearance_is_present) {
+ MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance);
+ }
+
+ if (fields->adv_itvl_is_present) {
+ MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl);
+ }
+
+ if (fields->svc_data_uuid32 != NULL) {
+ MODLOG_DFLT(DEBUG, " svc_data_uuid32=");
+ print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len);
+ MODLOG_DFLT(DEBUG, "\n");
+ }
+
+ if (fields->svc_data_uuid128 != NULL) {
+ MODLOG_DFLT(DEBUG, " svc_data_uuid128=");
+ print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len);
+ MODLOG_DFLT(DEBUG, "\n");
+ }
+
+ if (fields->uri != NULL) {
+ MODLOG_DFLT(DEBUG, " uri=");
+ print_bytes(fields->uri, fields->uri_len);
+ MODLOG_DFLT(DEBUG, "\n");
+ }
+
+ if (fields->mfg_data != NULL) {
+ MODLOG_DFLT(DEBUG, " mfg_data=");
+ print_bytes(fields->mfg_data, fields->mfg_data_len);
+ MODLOG_DFLT(DEBUG, "\n");
+ }
+}
diff --git a/src/libs/mynewt-nimble/apps/blecent/src/peer.c b/src/libs/mynewt-nimble/apps/blecent/src/peer.c
new file mode 100644
index 00000000..aeca7d90
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blecent/src/peer.c
@@ -0,0 +1,807 @@
+/*
+ * 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 "blecent.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_val_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;
+}
+
+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;
+}
diff --git a/src/libs/mynewt-nimble/apps/blecent/syscfg.yml b/src/libs/mynewt-nimble/apps/blecent/syscfg.yml
new file mode 100644
index 00000000..fee5bf06
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blecent/syscfg.yml
@@ -0,0 +1,30 @@
+# 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.vals:
+ # DEBUG logging is a bit noisy; use INFO.
+ LOG_LEVEL: 1
+
+ # Default task settings
+ OS_MAIN_STACK_SIZE: 336
+
+# Disable peripheral and broadcaster roles.
+ BLE_ROLE_BROADCASTER: 0
+ BLE_ROLE_CENTRAL: 1
+ BLE_ROLE_OBSERVER: 1
+ BLE_ROLE_PERIPHERAL: 0