summaryrefslogtreecommitdiff
path: root/src/libs/mynewt-nimble/apps
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/mynewt-nimble/apps')
-rw-r--r--src/libs/mynewt-nimble/apps/advertiser/pkg.yml33
-rw-r--r--src/libs/mynewt-nimble/apps/advertiser/src/main.c136
-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
-rw-r--r--src/libs/mynewt-nimble/apps/blecsc/README.md9
-rw-r--r--src/libs/mynewt-nimble/apps/blecsc/pkg.yml40
-rw-r--r--src/libs/mynewt-nimble/apps/blecsc/src/blecsc_sens.h105
-rw-r--r--src/libs/mynewt-nimble/apps/blecsc/src/gatt_svr.c385
-rw-r--r--src/libs/mynewt-nimble/apps/blecsc/src/main.c310
-rw-r--r--src/libs/mynewt-nimble/apps/blecsc/syscfg.yml38
-rw-r--r--src/libs/mynewt-nimble/apps/blehci/pkg.yml34
-rw-r--r--src/libs/mynewt-nimble/apps/blehci/src/main.c33
-rw-r--r--src/libs/mynewt-nimble/apps/blehci/syscfg.yml23
-rw-r--r--src/libs/mynewt-nimble/apps/blehr/README.md9
-rw-r--r--src/libs/mynewt-nimble/apps/blehr/pkg.yml40
-rw-r--r--src/libs/mynewt-nimble/apps/blehr/src/blehr_sens.h46
-rw-r--r--src/libs/mynewt-nimble/apps/blehr/src/gatt_svr.c177
-rw-r--r--src/libs/mynewt-nimble/apps/blehr/src/main.c261
-rw-r--r--src/libs/mynewt-nimble/apps/blehr/syscfg.yml35
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh/pkg.yml37
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh/src/main.c468
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh/syscfg.yml39
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_light/pkg.yml37
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.c242
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.h37
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_light/src/main.c113
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.c138
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.h42
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_light/syscfg.yml63
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_1/README.md79
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_1/pkg.yml34
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_1/src/main.c709
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_1/syscfg.yml38
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/README.md101
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/pkg.yml40
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.c95
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.h36
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.c116
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.h73
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/common.h33
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.c2779
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.h177
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/main.c250
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.c89
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.h34
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.c266
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.h46
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.c308
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.h53
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.c255
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.h47
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.c792
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.h87
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_models_example_2/syscfg.yml60
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_shell/pkg.yml37
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_shell/src/main.c114
-rw-r--r--src/libs/mynewt-nimble/apps/blemesh_shell/syscfg.yml57
-rw-r--r--src/libs/mynewt-nimble/apps/bleprph/pkg.yml45
-rw-r--r--src/libs/mynewt-nimble/apps/bleprph/src/bleprph.h61
-rw-r--r--src/libs/mynewt-nimble/apps/bleprph/src/gatt_svr.c204
-rw-r--r--src/libs/mynewt-nimble/apps/bleprph/src/main.c359
-rw-r--r--src/libs/mynewt-nimble/apps/bleprph/src/misc.c43
-rw-r--r--src/libs/mynewt-nimble/apps/bleprph/src/phy.c128
-rw-r--r--src/libs/mynewt-nimble/apps/bleprph/syscfg.yml68
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/README.md201
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/pkg.yml39
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/src/main.c99
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/src/misc.c224
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/src/misc.h54
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/src/rx_stress.c1471
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/src/rx_stress.h53
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/src/stress.c389
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/src/stress.h375
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.c155
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.h54
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/src/tx_stress.c1671
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/src/tx_stress.h49
-rw-r--r--src/libs/mynewt-nimble/apps/blestress/syscfg.yml74
-rw-r--r--src/libs/mynewt-nimble/apps/btshell/pkg.yml39
-rw-r--r--src/libs/mynewt-nimble/apps/btshell/src/btshell.h212
-rw-r--r--src/libs/mynewt-nimble/apps/btshell/src/cmd.c4659
-rw-r--r--src/libs/mynewt-nimble/apps/btshell/src/cmd.h68
-rw-r--r--src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.c587
-rw-r--r--src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.h39
-rw-r--r--src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.c325
-rw-r--r--src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.h33
-rw-r--r--src/libs/mynewt-nimble/apps/btshell/src/gatt_svr.c638
-rw-r--r--src/libs/mynewt-nimble/apps/btshell/src/main.c2630
-rw-r--r--src/libs/mynewt-nimble/apps/btshell/src/misc.c163
-rw-r--r--src/libs/mynewt-nimble/apps/btshell/src/parse.c734
-rw-r--r--src/libs/mynewt-nimble/apps/btshell/syscfg.yml42
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/README14
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/pkg.yml44
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/src/atomic.h405
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/src/bttester.c374
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/src/bttester.h1010
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/src/bttester_pipe.h40
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/src/gap.c1688
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/src/gatt.c2098
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/src/glue.c129
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/src/glue.h63
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/src/l2cap.c477
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/src/main.c72
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/src/mesh.c970
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/src/rtt_pipe.c136
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/src/uart_pipe.c281
-rw-r--r--src/libs/mynewt-nimble/apps/bttester/syscfg.yml122
-rw-r--r--src/libs/mynewt-nimble/apps/ext_advertiser/pkg.yml40
-rw-r--r--src/libs/mynewt-nimble/apps/ext_advertiser/src/main.c464
-rw-r--r--src/libs/mynewt-nimble/apps/ext_advertiser/src/patterns.h186
-rw-r--r--src/libs/mynewt-nimble/apps/ext_advertiser/syscfg.yml45
-rwxr-xr-xsrc/libs/mynewt-nimble/apps/peripheral/pkg.yml37
-rwxr-xr-xsrc/libs/mynewt-nimble/apps/peripheral/src/main.c166
-rw-r--r--src/libs/mynewt-nimble/apps/scanner/pkg.yml35
-rw-r--r--src/libs/mynewt-nimble/apps/scanner/src/main.c261
119 files changed, 36595 insertions, 0 deletions
diff --git a/src/libs/mynewt-nimble/apps/advertiser/pkg.yml b/src/libs/mynewt-nimble/apps/advertiser/pkg.yml
new file mode 100644
index 00000000..662e282e
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/advertiser/pkg.yml
@@ -0,0 +1,33 @@
+#
+# 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/advertiser"
+pkg.type: app
+pkg.description: "Basic advertiser application"
+pkg.author: "Krzysztof Kopyściński <krzysztof.kopyscinski@codecoup.pl>"
+
+pkg.deps:
+ - "@apache-mynewt-core/kernel/os"
+ - "@apache-mynewt-core/sys/console/full"
+ - "@apache-mynewt-core/sys/log/full"
+ - "@apache-mynewt-core/sys/stats/full"
+ - "@apache-mynewt-core/sys/log/modlog"
+ - "@apache-mynewt-nimble/nimble/host/util"
+ - "@apache-mynewt-nimble/nimble/host/services/gap"
+ - "@apache-mynewt-nimble/nimble/transport"
diff --git a/src/libs/mynewt-nimble/apps/advertiser/src/main.c b/src/libs/mynewt-nimble/apps/advertiser/src/main.c
new file mode 100644
index 00000000..486d5c59
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/advertiser/src/main.c
@@ -0,0 +1,136 @@
+/*
+ * 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 <stdlib.h>
+
+#include "os/os.h"
+#include "sysinit/sysinit.h"
+#include "log/log.h"
+#include "host/ble_hs.h"
+#include "host/util/util.h"
+#include "services/gap/ble_svc_gap.h"
+
+static const char *device_name = "Apache Mynewt";
+
+/* adv_event() calls advertise(), so forward declaration is required */
+static void advertise(void);
+
+static void
+set_ble_addr(void)
+{
+ int rc;
+ ble_addr_t addr;
+
+ /* generate new non-resolvable private address */
+ rc = ble_hs_id_gen_rnd(1, &addr);
+ assert(rc == 0);
+
+ /* set generated address */
+ rc = ble_hs_id_set_rnd(addr.val);
+ assert(rc == 0);
+}
+
+static int
+adv_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_ADV_COMPLETE:
+ MODLOG_DFLT(INFO, "Advertising completed, termination code: %d\n",
+ event->adv_complete.reason);
+ advertise();
+ return 0;
+ default:
+ MODLOG_DFLT(ERROR, "Advertising event not handled\n");
+ return 0;
+ }
+}
+
+static void
+advertise(void)
+{
+ int rc;
+ struct ble_gap_adv_params adv_params;
+ struct ble_hs_adv_fields fields;
+
+ /* set adv parameters */
+ memset(&adv_params, 0, sizeof(adv_params));
+ adv_params.conn_mode = BLE_GAP_CONN_MODE_NON;
+ adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
+
+ memset(&fields, 0, sizeof(fields));
+
+ /* Fill the fields with advertising data - flags, tx power level, name */
+ fields.flags = BLE_HS_ADV_F_DISC_GEN;
+ fields.tx_pwr_lvl_is_present = 1;
+ fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
+ fields.name = (uint8_t *)device_name;
+ fields.name_len = strlen(device_name);
+ fields.name_is_complete = 1;
+
+ rc = ble_gap_adv_set_fields(&fields);
+ assert(rc == 0);
+
+ MODLOG_DFLT(INFO, "Starting advertising...\n");
+
+ /* As own address type we use hard-coded value, because we generate
+ NRPA and by definition it's random */
+ rc = ble_gap_adv_start(BLE_OWN_ADDR_RANDOM, NULL, 10000,
+ &adv_params, adv_event, NULL);
+ assert(rc == 0);
+}
+
+static void
+on_sync(void)
+{
+ set_ble_addr();
+
+ /* begin advertising */
+ advertise();
+}
+
+static void
+on_reset(int reason)
+{
+ MODLOG_DFLT(INFO, "Resetting state; reason=%d\n", reason);
+}
+
+int
+main(int argc, char **argv)
+{
+ int rc;
+
+ /* Initialize all packages. */
+ sysinit();
+
+ ble_hs_cfg.sync_cb = on_sync;
+ ble_hs_cfg.reset_cb = on_reset;
+
+ rc = ble_svc_gap_device_name_set(device_name);
+ assert(rc == 0);
+
+ /* As the last thing, process events from default event queue. */
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+
+ return 0;
+}
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
diff --git a/src/libs/mynewt-nimble/apps/blecsc/README.md b/src/libs/mynewt-nimble/apps/blecsc/README.md
new file mode 100644
index 00000000..bccf176a
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blecsc/README.md
@@ -0,0 +1,9 @@
+# BLE Cycling Speed and Cadence peripheral app.
+
+The source files are located in the src/ directory.
+
+pkg.yml contains the base definition of the app.
+
+syscfg.yml contains setting definitions and overrides.
+
+
diff --git a/src/libs/mynewt-nimble/apps/blecsc/pkg.yml b/src/libs/mynewt-nimble/apps/blecsc/pkg.yml
new file mode 100644
index 00000000..828ff301
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blecsc/pkg.yml
@@ -0,0 +1,40 @@
+# 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/blecsc
+pkg.type: app
+pkg.description: BLE peripheral cycling speed and cadence sensor.
+pkg.author: "Maciej Jurczak"
+pkg.email: "mjurczak@gmail.com"
+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"
+ - "@apache-mynewt-core/sys/sysinit"
+ - "@apache-mynewt-core/sys/id"
+ - nimble/controller
+ - nimble/host
+ - nimble/host/services/gap
+ - nimble/host/services/gatt
+ - nimble/host/store/config
+ - nimble/transport
diff --git a/src/libs/mynewt-nimble/apps/blecsc/src/blecsc_sens.h b/src/libs/mynewt-nimble/apps/blecsc/src/blecsc_sens.h
new file mode 100644
index 00000000..99addc0f
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blecsc/src/blecsc_sens.h
@@ -0,0 +1,105 @@
+/*
+ * 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_BLECSC_SENSOR_
+#define H_BLECSC_SENSOR_
+
+#include "modlog/modlog.h"
+#include "nimble/ble.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Cycling Speed and Cadence configuration */
+#define GATT_CSC_UUID 0x1816
+#define GATT_CSC_MEASUREMENT_UUID 0x2A5B
+#define GATT_CSC_FEATURE_UUID 0x2A5C
+#define GATT_SENSOR_LOCATION_UUID 0x2A5D
+#define GATT_SC_CONTROL_POINT_UUID 0x2A55
+/* Device Information configuration */
+#define GATT_DEVICE_INFO_UUID 0x180A
+#define GATT_MANUFACTURER_NAME_UUID 0x2A29
+#define GATT_MODEL_NUMBER_UUID 0x2A24
+
+/*CSC Measurement flags*/
+#define CSC_MEASUREMENT_WHEEL_REV_PRESENT 0x01
+#define CSC_MEASUREMENT_CRANK_REV_PRESENT 0x02
+
+/* CSC feature flags */
+#define CSC_FEATURE_WHEEL_REV_DATA 0x01
+#define CSC_FEATURE_CRANK_REV_DATA 0x02
+#define CSC_FEATURE_MULTIPLE_SENSOR_LOC 0x04
+
+/* Sensor location enum */
+#define SENSOR_LOCATION_OTHER 0
+#define SENSOR_LOCATION_TOP_OF_SHOE 1
+#define SENSOR_LOCATION_IN_SHOE 2
+#define SENSOR_LOCATION_HIP 3
+#define SENSOR_LOCATION_FRONT_WHEEL 4
+#define SENSOR_LOCATION_LEFT_CRANK 5
+#define SENSOR_LOCATION_RIGHT_CRANK 6
+#define SENSOR_LOCATION_LEFT_PEDAL 7
+#define SENSOR_LOCATION_RIGHT_PEDAL 8
+#define SENSOR_LOCATION_FROT_HUB 9
+#define SENSOR_LOCATION_REAR_DROPOUT 10
+#define SENSOR_LOCATION_CHAINSTAY 11
+#define SENSOR_LOCATION_REAR_WHEEL 12
+#define SENSOR_LOCATION_REAR_HUB 13
+#define SENSOR_LOCATION_CHEST 14
+#define SENSOR_LOCATION_SPIDER 15
+#define SENSOR_LOCATION_CHAIN_RING 16
+
+/* SC Control Point op codes */
+#define SC_CP_OP_SET_CUMULATIVE_VALUE 1
+#define SC_CP_OP_START_SENSOR_CALIBRATION 2
+#define SC_CP_OP_UPDATE_SENSOR_LOCATION 3
+#define SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS 4
+#define SC_CP_OP_RESPONSE 16
+
+/*SC Control Point response values */
+#define SC_CP_RESPONSE_SUCCESS 1
+#define SC_CP_RESPONSE_OP_NOT_SUPPORTED 2
+#define SC_CP_RESPONSE_INVALID_PARAM 3
+#define SC_CP_RESPONSE_OP_FAILED 4
+
+/* CSC simulation configuration */
+#define CSC_FEATURES (CSC_FEATURE_WHEEL_REV_DATA | \
+ CSC_FEATURE_CRANK_REV_DATA |\
+ CSC_FEATURE_MULTIPLE_SENSOR_LOC)
+
+struct ble_csc_measurement_state {
+ uint32_t cumulative_wheel_rev;
+ uint16_t last_wheel_evt_time;
+ uint16_t cumulative_crank_rev;
+ uint16_t last_crank_evt_time;
+};
+
+extern uint16_t csc_measurement_handle;
+extern uint16_t csc_control_point_handle;
+
+int gatt_svr_init(struct ble_csc_measurement_state * csc_measurement_state);
+int gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle);
+void gatt_svr_set_cp_indicate(uint8_t indication_status);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/blecsc/src/gatt_svr.c b/src/libs/mynewt-nimble/apps/blecsc/src/gatt_svr.c
new file mode 100644
index 00000000..e66aa9a8
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blecsc/src/gatt_svr.c
@@ -0,0 +1,385 @@
+/*
+ * 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 "os/mynewt.h"
+#include "host/ble_hs.h"
+#include "host/ble_uuid.h"
+#include "blecsc_sens.h"
+
+#define CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED 0x81
+
+static const char *manuf_name = "Apache Mynewt";
+static const char *model_num = "Mynewt CSC Sensor";
+
+static const uint8_t csc_supported_sensor_locations[] = {
+ SENSOR_LOCATION_FRONT_WHEEL,
+ SENSOR_LOCATION_REAR_DROPOUT,
+ SENSOR_LOCATION_CHAINSTAY,
+ SENSOR_LOCATION_REAR_WHEEL
+};
+
+static uint8_t sensor_location = SENSOR_LOCATION_REAR_DROPOUT;
+static struct ble_csc_measurement_state * measurement_state;
+uint16_t csc_measurement_handle;
+uint16_t csc_control_point_handle;
+uint8_t csc_cp_indication_status;
+
+static int
+gatt_svr_chr_access_csc_measurement(uint16_t conn_handle,
+ uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_chr_access_csc_feature(uint16_t conn_handle,
+ uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_chr_access_sensor_location(uint16_t conn_handle,
+ uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
+ uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_chr_access_device_info(uint16_t conn_handle,
+ uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
+ {
+ /* Service: Cycling Speed and Cadence */
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = BLE_UUID16_DECLARE(GATT_CSC_UUID),
+ .characteristics = (struct ble_gatt_chr_def[]) { {
+ /* Characteristic: Cycling Speed and Cadence Measurement */
+ .uuid = BLE_UUID16_DECLARE(GATT_CSC_MEASUREMENT_UUID),
+ .access_cb = gatt_svr_chr_access_csc_measurement,
+ .val_handle = &csc_measurement_handle,
+ .flags = BLE_GATT_CHR_F_NOTIFY,
+ }, {
+ /* Characteristic: Cycling Speed and Cadence features */
+ .uuid = BLE_UUID16_DECLARE(GATT_CSC_FEATURE_UUID),
+ .access_cb = gatt_svr_chr_access_csc_feature,
+ .flags = BLE_GATT_CHR_F_READ,
+ }, {
+ /* Characteristic: Sensor Location */
+ .uuid = BLE_UUID16_DECLARE(GATT_SENSOR_LOCATION_UUID),
+ .access_cb = gatt_svr_chr_access_sensor_location,
+ .flags = BLE_GATT_CHR_F_READ,
+ }, {
+ /* Characteristic: SC Control Point*/
+ .uuid = BLE_UUID16_DECLARE(GATT_SC_CONTROL_POINT_UUID),
+ .access_cb = gatt_svr_chr_access_sc_control_point,
+ .val_handle = &csc_control_point_handle,
+ .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_INDICATE,
+ }, {
+ 0, /* No more characteristics in this service */
+ }, }
+ },
+
+ {
+ /* Service: Device Information */
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID),
+ .characteristics = (struct ble_gatt_chr_def[]) { {
+ /* Characteristic: * Manufacturer name */
+ .uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID),
+ .access_cb = gatt_svr_chr_access_device_info,
+ .flags = BLE_GATT_CHR_F_READ,
+ }, {
+ /* Characteristic: Model number string */
+ .uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID),
+ .access_cb = gatt_svr_chr_access_device_info,
+ .flags = BLE_GATT_CHR_F_READ,
+ }, {
+ 0, /* No more characteristics in this service */
+ }, }
+ },
+
+ {
+ 0, /* No more services */
+ },
+};
+
+static int
+gatt_svr_chr_access_csc_measurement(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+ return BLE_ATT_ERR_READ_NOT_PERMITTED;
+}
+
+static int
+gatt_svr_chr_access_csc_feature(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+ static const uint16_t csc_feature = CSC_FEATURES;
+ int rc;
+
+ assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
+ rc = os_mbuf_append(ctxt->om, &csc_feature, sizeof(csc_feature));
+
+ return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+}
+
+static int
+gatt_svr_chr_access_sensor_location(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+ int rc;
+
+ assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
+ rc = os_mbuf_append(ctxt->om, &sensor_location, sizeof(sensor_location));
+
+ return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+}
+
+static int
+gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
+ uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ uint8_t op_code;
+ uint8_t new_sensor_location;
+ uint8_t new_cumulative_wheel_rev_arr[4];
+ struct os_mbuf *om_indication;
+ uint8_t response = SC_CP_RESPONSE_OP_NOT_SUPPORTED;
+ int ii;
+ int rc;
+
+ assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR);
+
+ if (!csc_cp_indication_status) {
+ MODLOG_DFLT(INFO, "SC Control Point; CCC descriptor "
+ "improperly configured");
+ return CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED;
+ }
+
+ /* Read control point op code*/
+ rc = os_mbuf_copydata(ctxt->om, 0, sizeof(op_code), &op_code);
+ if (rc != 0){
+ return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
+ }
+ MODLOG_DFLT(INFO, "SC Control Point; opcode=%d\n", op_code);
+
+ /* Allocate response buffer */
+ om_indication = ble_hs_mbuf_att_pkt();
+
+ switch(op_code){
+#if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA)
+ case SC_CP_OP_SET_CUMULATIVE_VALUE:
+ /* Read new cumulative wheel revolutions value*/
+ rc = os_mbuf_copydata(ctxt->om, 1,
+ sizeof(new_cumulative_wheel_rev_arr),
+ new_cumulative_wheel_rev_arr);
+ if (rc != 0){
+ return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
+ }
+
+ measurement_state->cumulative_wheel_rev =
+ get_le32(new_cumulative_wheel_rev_arr);
+
+ MODLOG_DFLT(INFO, "SC Control Point; Set cumulative value = %d\n",
+ measurement_state->cumulative_wheel_rev);
+
+ response = SC_CP_RESPONSE_SUCCESS;
+ break;
+#endif
+
+#if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC)
+ case SC_CP_OP_UPDATE_SENSOR_LOCATION:
+ /* Read new sensor location value*/
+ rc = os_mbuf_copydata(ctxt->om, 1, 1, &new_sensor_location);
+ if (rc != 0){
+ return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
+ }
+
+ MODLOG_DFLT(INFO, "SC Control Point; Sensor location update = %d\n",
+ new_sensor_location);
+
+ /* Verify if requested new location is on supported locations list */
+ response = SC_CP_RESPONSE_INVALID_PARAM;
+ for (ii = 0; ii < sizeof(csc_supported_sensor_locations); ii++){
+ if (new_sensor_location == csc_supported_sensor_locations[ii]){
+ sensor_location = new_sensor_location;
+ response = SC_CP_RESPONSE_SUCCESS;
+ break;
+ }
+ }
+ break;
+
+ case SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS:
+ response = SC_CP_RESPONSE_SUCCESS;
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ /* Append response value */
+ rc = os_mbuf_append(om_indication, &response, sizeof(response));
+
+ if (rc != 0){
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+#if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC)
+ /* In case of supported locations request append locations list */
+ if (op_code == SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS){
+ rc = os_mbuf_append(om_indication, &csc_supported_sensor_locations,
+ sizeof(csc_supported_sensor_locations));
+ }
+
+ if (rc != 0){
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+#endif
+
+ rc = ble_gattc_indicate_custom(conn_handle, csc_control_point_handle,
+ om_indication);
+
+ return rc;
+}
+
+static int
+gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+ uint16_t uuid;
+ int rc;
+
+ uuid = ble_uuid_u16(ctxt->chr->uuid);
+
+ if (uuid == GATT_MODEL_NUMBER_UUID) {
+ rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num));
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ if (uuid == GATT_MANUFACTURER_NAME_UUID) {
+ rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name));
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+}
+
+int
+gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle)
+{
+ int rc;
+ struct os_mbuf *om;
+ uint8_t data_buf[11];
+ uint8_t data_offset = 1;
+
+ memset(data_buf, 0, sizeof(data_buf));
+
+#if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA)
+ data_buf[0] |= CSC_MEASUREMENT_WHEEL_REV_PRESENT;
+ put_le16(&(data_buf[5]), measurement_state->last_wheel_evt_time);
+ put_le32(&(data_buf[1]), measurement_state->cumulative_wheel_rev);
+ data_offset += 6;
+#endif
+
+#if (CSC_FEATURES & CSC_FEATURE_CRANK_REV_DATA)
+ data_buf[0] |= CSC_MEASUREMENT_CRANK_REV_PRESENT;
+ put_le16(&(data_buf[data_offset]),
+ measurement_state->cumulative_crank_rev);
+ put_le16(&(data_buf[data_offset + 2]),
+ measurement_state->last_crank_evt_time);
+ data_offset += 4;
+#endif
+
+ om = ble_hs_mbuf_from_flat(data_buf, data_offset);
+
+ rc = ble_gattc_notify_custom(conn_handle, csc_measurement_handle, om);
+ return rc;
+}
+
+void
+gatt_svr_set_cp_indicate(uint8_t indication_status)
+{
+ csc_cp_indication_status = indication_status;
+}
+
+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:
+ MODLOG_DFLT(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:
+ MODLOG_DFLT(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:
+ MODLOG_DFLT(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(struct ble_csc_measurement_state * csc_measurement_state)
+{
+ 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;
+ }
+
+ measurement_state = csc_measurement_state;
+
+ return 0;
+}
+
diff --git a/src/libs/mynewt-nimble/apps/blecsc/src/main.c b/src/libs/mynewt-nimble/apps/blecsc/src/main.c
new file mode 100644
index 00000000..60f0b3d8
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blecsc/src/main.c
@@ -0,0 +1,310 @@
+/*
+ * 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 "os/mynewt.h"
+#include "console/console.h"
+#include "config/config.h"
+#include "nimble/ble.h"
+#include "host/ble_hs.h"
+#include "services/gap/ble_svc_gap.h"
+#include "blecsc_sens.h"
+
+/* Wheel size for simulation calculations */
+#define CSC_SIM_WHEEL_CIRCUMFERENCE_MM 2000
+/* Simulated cadence lower limit */
+#define CSC_SIM_CRANK_RPM_MIN 20
+/* Simulated cadence upper limit */
+#define CSC_SIM_CRANK_RPM_MAX 100
+/* Simulated speed lower limit */
+#define CSC_SIM_SPEED_KPH_MIN 0
+/* Simulated speed upper limit */
+#define CSC_SIM_SPEED_KPH_MAX 35
+
+/* Noticication status */
+static bool notify_state = false;
+
+/* Connection handle */
+static uint16_t conn_handle;
+
+static uint8_t blecsc_addr_type;
+
+/* Advertised device name */
+static const char *device_name = "blecsc_sensor";
+
+/* Measurement and notification timer */
+static struct os_callout blecsc_measure_timer;
+
+/* Variable holds current CSC measurement state */
+static struct ble_csc_measurement_state csc_measurement_state;
+
+/* Variable holds simulted speed (kilometers per hour) */
+static uint16_t csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN;
+
+/* Variable holds simulated cadence (RPM) */
+static uint8_t csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN;
+
+static int blecsc_gap_event(struct ble_gap_event *event, void *arg);
+
+
+/*
+ * Enables advertising with parameters:
+ * o General discoverable mode
+ * o Undirected connectable mode
+ */
+static void
+blecsc_advertise(void)
+{
+ struct ble_gap_adv_params adv_params;
+ struct ble_hs_adv_fields fields;
+ 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
+ */
+ 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 assigning 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;
+
+ fields.name = (uint8_t *)device_name;
+ fields.name_len = strlen(device_name);
+ fields.name_is_complete = 1;
+
+ /*
+ * Set appearance.
+ */
+ fields.appearance = ble_svc_gap_device_appearance();
+ fields.appearance_is_present = 1;
+
+ rc = ble_gap_adv_set_fields(&fields);
+ if (rc != 0) {
+ MODLOG_DFLT(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_UND;
+ adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
+ rc = ble_gap_adv_start(blecsc_addr_type, NULL, BLE_HS_FOREVER,
+ &adv_params, blecsc_gap_event, NULL);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
+ return;
+ }
+}
+
+
+/* Update simulated CSC measurements.
+ * Each call increments wheel and crank revolution counters by one and
+ * computes last event time in order to match simulated candence and speed.
+ * Last event time is expressedd in 1/1024th of second units.
+ *
+ * 60 * 1024
+ * crank_dt = --------------
+ * cadence[RPM]
+ *
+ *
+ * circumference[mm] * 1024 * 60 * 60
+ * wheel_dt = -------------------------------------
+ * 10^6 * speed [kph]
+ */
+static void
+blecsc_simulate_speed_and_cadence(void)
+{
+ uint16_t wheel_rev_period;
+ uint16_t crank_rev_period;
+
+ /* Update simulated crank and wheel rotation speed */
+ csc_sim_speed_kph++;
+ if (csc_sim_speed_kph >= CSC_SIM_SPEED_KPH_MAX) {
+ csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN;
+ }
+
+ csc_sim_crank_rpm++;
+ if (csc_sim_crank_rpm >= CSC_SIM_CRANK_RPM_MAX) {
+ csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN;
+ }
+
+ /* Calculate simulated measurement values */
+ if (csc_sim_speed_kph > 0){
+ wheel_rev_period = (36*64*CSC_SIM_WHEEL_CIRCUMFERENCE_MM) /
+ (625*csc_sim_speed_kph);
+ csc_measurement_state.cumulative_wheel_rev++;
+ csc_measurement_state.last_wheel_evt_time += wheel_rev_period;
+ }
+
+ if (csc_sim_crank_rpm > 0){
+ crank_rev_period = (60*1024) / csc_sim_crank_rpm;
+ csc_measurement_state.cumulative_crank_rev++;
+ csc_measurement_state.last_crank_evt_time += crank_rev_period;
+ }
+
+ MODLOG_DFLT(INFO, "CSC simulated values: speed = %d kph, cadence = %d \n",
+ csc_sim_speed_kph, csc_sim_crank_rpm);
+}
+
+/* Run CSC measurement simulation and notify it to the client */
+static void
+blecsc_measurement(struct os_event *ev)
+{
+ int rc;
+
+ rc = os_callout_reset(&blecsc_measure_timer, OS_TICKS_PER_SEC);
+ assert(rc == 0);
+
+ blecsc_simulate_speed_and_cadence();
+
+ if (notify_state) {
+ rc = gatt_svr_chr_notify_csc_measurement(conn_handle);
+ assert(rc == 0);
+ }
+}
+
+static int
+blecsc_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed */
+ MODLOG_DFLT(INFO, "connection %s; status=%d\n",
+ event->connect.status == 0 ? "established" : "failed",
+ event->connect.status);
+
+ if (event->connect.status != 0) {
+ /* Connection failed; resume advertising */
+ blecsc_advertise();
+ conn_handle = 0;
+ }
+ else {
+ conn_handle = event->connect.conn_handle;
+ }
+ break;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason);
+ conn_handle = 0;
+ /* Connection terminated; resume advertising */
+ blecsc_advertise();
+ break;
+
+ case BLE_GAP_EVENT_ADV_COMPLETE:
+ MODLOG_DFLT(INFO, "adv complete\n");
+ break;
+
+ case BLE_GAP_EVENT_SUBSCRIBE:
+ MODLOG_DFLT(INFO, "subscribe event attr_handle=%d\n",
+ event->subscribe.attr_handle);
+
+ if (event->subscribe.attr_handle == csc_measurement_handle) {
+ notify_state = event->subscribe.cur_notify;
+ MODLOG_DFLT(INFO, "csc measurement notify state = %d\n",
+ notify_state);
+ }
+ else if (event->subscribe.attr_handle == csc_control_point_handle) {
+ gatt_svr_set_cp_indicate(event->subscribe.cur_indicate);
+ MODLOG_DFLT(INFO, "csc control point indicate state = %d\n",
+ event->subscribe.cur_indicate);
+ }
+ break;
+
+ case BLE_GAP_EVENT_MTU:
+ MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n",
+ event->mtu.conn_handle,
+ event->mtu.value);
+ break;
+
+ }
+
+ return 0;
+}
+
+static void
+blecsc_on_sync(void)
+{
+ int rc;
+
+ /* Figure out address to use while advertising (no privacy) */
+ rc = ble_hs_id_infer_auto(0, &blecsc_addr_type);
+ assert(rc == 0);
+
+ /* Begin advertising */
+ blecsc_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();
+
+ /* Initialize the NimBLE host configuration */
+ ble_hs_cfg.sync_cb = blecsc_on_sync;
+
+ /* Initialize measurement and notification timer */
+ os_callout_init(&blecsc_measure_timer, os_eventq_dflt_get(),
+ blecsc_measurement, NULL);
+ rc = os_callout_reset(&blecsc_measure_timer, OS_TICKS_PER_SEC);
+ assert(rc == 0);
+
+ rc = gatt_svr_init(&csc_measurement_state);
+ assert(rc == 0);
+
+ /* Set the default device name */
+ rc = ble_svc_gap_device_name_set(device_name);
+ assert(rc == 0);
+
+ /* As the last thing, process events from default event queue */
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+ return 0;
+}
+
diff --git a/src/libs/mynewt-nimble/apps/blecsc/syscfg.yml b/src/libs/mynewt-nimble/apps/blecsc/syscfg.yml
new file mode 100644
index 00000000..2f41785b
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blecsc/syscfg.yml
@@ -0,0 +1,38 @@
+# 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:
+ # 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
+
+ # Log reboot messages to a flash circular buffer.
+ REBOOT_LOG_FCB: 1
+ LOG_FCB: 1
+ CONFIG_FCB: 1
+
+ # Set public device address.
+ BLE_PUBLIC_DEV_ADDR: ((uint8_t[6]){0xcc, 0xbb, 0xaa, 0x33, 0x22, 0x11})
+
+ # Set device appearance to Cycling Speed and Cadence Sensor
+ BLE_SVC_GAP_APPEARANCE: BLE_SVC_GAP_APPEARANCE_CYC_SPEED_AND_CADENCE_SENSOR
diff --git a/src/libs/mynewt-nimble/apps/blehci/pkg.yml b/src/libs/mynewt-nimble/apps/blehci/pkg.yml
new file mode 100644
index 00000000..06e61894
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blehci/pkg.yml
@@ -0,0 +1,34 @@
+# 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/blehci
+pkg.type: app
+pkg.description: BLE controller application exposing HCI over external interface
+pkg.author: "Johan Hedberg <johan.hedberg@intel.com>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+ - "@apache-mynewt-core/sys/console/stub"
+ - "@apache-mynewt-core/sys/log/stub"
+ - "@apache-mynewt-core/sys/stats/full"
+ - "@apache-mynewt-core/kernel/os"
+ - nimble/controller
+ - nimble/transport
+
+pkg.req_apis:
+ - ble_transport
diff --git a/src/libs/mynewt-nimble/apps/blehci/src/main.c b/src/libs/mynewt-nimble/apps/blehci/src/main.c
new file mode 100644
index 00000000..040c1572
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blehci/src/main.c
@@ -0,0 +1,33 @@
+/*
+ * 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 "os/mynewt.h"
+
+int
+main(void)
+{
+ /* Initialize OS */
+ sysinit();
+
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/blehci/syscfg.yml b/src/libs/mynewt-nimble/apps/blehci/syscfg.yml
new file mode 100644
index 00000000..48a02005
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blehci/syscfg.yml
@@ -0,0 +1,23 @@
+# 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:
+ # Default task settings
+ OS_MAIN_STACK_SIZE: 64
+ # Use UART transport by default
+ BLE_HCI_TRANSPORT: uart
diff --git a/src/libs/mynewt-nimble/apps/blehr/README.md b/src/libs/mynewt-nimble/apps/blehr/README.md
new file mode 100644
index 00000000..d06d95de
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blehr/README.md
@@ -0,0 +1,9 @@
+# BLE Heart Rate peripheral app.
+
+The source files are located in the src/ directory.
+
+pkg.yml contains the base definition of the app.
+
+syscfg.yml contains setting definitions and overrides.
+
+
diff --git a/src/libs/mynewt-nimble/apps/blehr/pkg.yml b/src/libs/mynewt-nimble/apps/blehr/pkg.yml
new file mode 100644
index 00000000..b91ba377
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blehr/pkg.yml
@@ -0,0 +1,40 @@
+# 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/blehr
+pkg.type: app
+pkg.description: BLE peripheral heartrate sensor.
+pkg.author: "Szymon Czapracki"
+pkg.email: "szymon.czapracki@codecoup.pl"
+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"
+ - "@apache-mynewt-core/sys/sysinit"
+ - "@apache-mynewt-core/sys/id"
+ - nimble/controller
+ - nimble/host
+ - nimble/host/services/gap
+ - nimble/host/services/gatt
+ - nimble/host/store/config
+ - nimble/transport/ram
diff --git a/src/libs/mynewt-nimble/apps/blehr/src/blehr_sens.h b/src/libs/mynewt-nimble/apps/blehr/src/blehr_sens.h
new file mode 100644
index 00000000..a3f59ca9
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blehr/src/blehr_sens.h
@@ -0,0 +1,46 @@
+/*
+ * 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_BLEHR_SENSOR_
+#define H_BLEHR_SENSOR_
+
+#include "nimble/ble.h"
+#include "modlog/modlog.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Heart-rate configuration */
+#define GATT_HRS_UUID 0x180D
+#define GATT_HRS_MEASUREMENT_UUID 0x2A37
+#define GATT_HRS_BODY_SENSOR_LOC_UUID 0x2A38
+#define GATT_DEVICE_INFO_UUID 0x180A
+#define GATT_MANUFACTURER_NAME_UUID 0x2A29
+#define GATT_MODEL_NUMBER_UUID 0x2A24
+
+extern uint16_t hrs_hrm_handle;
+
+int gatt_svr_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/blehr/src/gatt_svr.c b/src/libs/mynewt-nimble/apps/blehr/src/gatt_svr.c
new file mode 100644
index 00000000..b2e9b4e0
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blehr/src/gatt_svr.c
@@ -0,0 +1,177 @@
+/*
+ * 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 "blehr_sens.h"
+
+static const char *manuf_name = "Apache Mynewt";
+static const char *model_num = "Mynewt HR Sensor";
+uint16_t hrs_hrm_handle;
+
+static int
+gatt_svr_chr_access_heart_rate(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg);
+
+static int
+gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg);
+
+static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
+ {
+ /* Service: Heart-rate */
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = BLE_UUID16_DECLARE(GATT_HRS_UUID),
+ .characteristics = (struct ble_gatt_chr_def[]) { {
+ /* Characteristic: Heart-rate measurement */
+ .uuid = BLE_UUID16_DECLARE(GATT_HRS_MEASUREMENT_UUID),
+ .access_cb = gatt_svr_chr_access_heart_rate,
+ .val_handle = &hrs_hrm_handle,
+ .flags = BLE_GATT_CHR_F_NOTIFY,
+ }, {
+ /* Characteristic: Body sensor location */
+ .uuid = BLE_UUID16_DECLARE(GATT_HRS_BODY_SENSOR_LOC_UUID),
+ .access_cb = gatt_svr_chr_access_heart_rate,
+ .flags = BLE_GATT_CHR_F_READ,
+ }, {
+ 0, /* No more characteristics in this service */
+ }, }
+ },
+
+ {
+ /* Service: Device Information */
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID),
+ .characteristics = (struct ble_gatt_chr_def[]) { {
+ /* Characteristic: * Manufacturer name */
+ .uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID),
+ .access_cb = gatt_svr_chr_access_device_info,
+ .flags = BLE_GATT_CHR_F_READ,
+ }, {
+ /* Characteristic: Model number string */
+ .uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID),
+ .access_cb = gatt_svr_chr_access_device_info,
+ .flags = BLE_GATT_CHR_F_READ,
+ }, {
+ 0, /* No more characteristics in this service */
+ }, }
+ },
+
+ {
+ 0, /* No more services */
+ },
+};
+
+static int
+gatt_svr_chr_access_heart_rate(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+ /* Sensor location, set to "Chest" */
+ static uint8_t body_sens_loc = 0x01;
+ uint16_t uuid;
+ int rc;
+
+ uuid = ble_uuid_u16(ctxt->chr->uuid);
+
+ if (uuid == GATT_HRS_BODY_SENSOR_LOC_UUID) {
+ rc = os_mbuf_append(ctxt->om, &body_sens_loc, sizeof(body_sens_loc));
+
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+}
+
+static int
+gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+ uint16_t uuid;
+ int rc;
+
+ uuid = ble_uuid_u16(ctxt->chr->uuid);
+
+ if (uuid == GATT_MODEL_NUMBER_UUID) {
+ rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num));
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ if (uuid == GATT_MANUFACTURER_NAME_UUID) {
+ rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name));
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ assert(0);
+ 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:
+ MODLOG_DFLT(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:
+ MODLOG_DFLT(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:
+ MODLOG_DFLT(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;
+}
+
diff --git a/src/libs/mynewt-nimble/apps/blehr/src/main.c b/src/libs/mynewt-nimble/apps/blehr/src/main.c
new file mode 100644
index 00000000..bf0f3f30
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blehr/src/main.c
@@ -0,0 +1,261 @@
+/*
+ * 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 "os/mynewt.h"
+#include "console/console.h"
+#include "config/config.h"
+#include "nimble/ble.h"
+#include "host/ble_hs.h"
+#include "services/gap/ble_svc_gap.h"
+#include "blehr_sens.h"
+
+static bool notify_state;
+
+/* Connection handle */
+static uint16_t conn_handle;
+
+static const char *device_name = "blehr_sensor";
+
+static int blehr_gap_event(struct ble_gap_event *event, void *arg);
+
+static uint8_t blehr_addr_type;
+
+/* Sending notify data timer */
+static struct os_callout blehr_tx_timer;
+
+/* Variable to simulate heart beats */
+static uint8_t heartrate = 90;
+
+/*
+ * Enables advertising with parameters:
+ * o General discoverable mode
+ * o Undirected connectable mode
+ */
+static void
+blehr_advertise(void)
+{
+ struct ble_gap_adv_params adv_params;
+ struct ble_hs_adv_fields fields;
+ 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
+ */
+ 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 assigning 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;
+
+ fields.name = (uint8_t *)device_name;
+ fields.name_len = strlen(device_name);
+ fields.name_is_complete = 1;
+
+ rc = ble_gap_adv_set_fields(&fields);
+ if (rc != 0) {
+ MODLOG_DFLT(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_UND;
+ adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
+ rc = ble_gap_adv_start(blehr_addr_type, NULL, BLE_HS_FOREVER,
+ &adv_params, blehr_gap_event, NULL);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
+ return;
+ }
+}
+
+static void
+blehr_tx_hrate_stop(void)
+{
+ os_callout_stop(&blehr_tx_timer);
+}
+
+/* Reset heartrate measurment */
+static void
+blehr_tx_hrate_reset(void)
+{
+ int rc;
+
+ rc = os_callout_reset(&blehr_tx_timer, OS_TICKS_PER_SEC);
+ assert(rc == 0);
+}
+
+/* This functions simulates heart beat and notifies it to the client */
+static void
+blehr_tx_hrate(struct os_event *ev)
+{
+ static uint8_t hrm[2];
+ int rc;
+ struct os_mbuf *om;
+
+ if (!notify_state) {
+ blehr_tx_hrate_stop();
+ heartrate = 90;
+ return;
+ }
+
+ hrm[0] = 0x06; /* contact of a sensor */
+ hrm[1] = heartrate; /* storing dummy data */
+
+ /* Simulation of heart beats */
+ heartrate++;
+ if (heartrate == 160) {
+ heartrate = 90;
+ }
+
+ om = ble_hs_mbuf_from_flat(hrm, sizeof(hrm));
+
+ rc = ble_gattc_notify_custom(conn_handle, hrs_hrm_handle, om);
+
+ assert(rc == 0);
+ blehr_tx_hrate_reset();
+}
+
+static int
+blehr_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed */
+ MODLOG_DFLT(INFO, "connection %s; status=%d\n",
+ event->connect.status == 0 ? "established" : "failed",
+ event->connect.status);
+
+ if (event->connect.status != 0) {
+ /* Connection failed; resume advertising */
+ blehr_advertise();
+ conn_handle = 0;
+ }
+ else {
+ conn_handle = event->connect.conn_handle;
+ }
+
+ break;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason);
+ conn_handle = BLE_HS_CONN_HANDLE_NONE; /* reset conn_handle */
+
+ /* Connection terminated; resume advertising */
+ blehr_advertise();
+ break;
+
+ case BLE_GAP_EVENT_ADV_COMPLETE:
+ MODLOG_DFLT(INFO, "adv complete\n");
+ blehr_advertise();
+ break;
+
+ case BLE_GAP_EVENT_SUBSCRIBE:
+ MODLOG_DFLT(INFO, "subscribe event; cur_notify=%d\n value handle; "
+ "val_handle=%d\n",
+ event->subscribe.cur_notify, hrs_hrm_handle);
+ if (event->subscribe.attr_handle == hrs_hrm_handle) {
+ notify_state = event->subscribe.cur_notify;
+ blehr_tx_hrate_reset();
+ } else if (event->subscribe.attr_handle != hrs_hrm_handle) {
+ notify_state = event->subscribe.cur_notify;
+ blehr_tx_hrate_stop();
+ }
+ break;
+
+ case BLE_GAP_EVENT_MTU:
+ MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n",
+ event->mtu.conn_handle,
+ event->mtu.value);
+ break;
+
+ }
+
+ return 0;
+}
+
+static void
+blehr_on_sync(void)
+{
+ int rc;
+
+ /* Use privacy */
+ rc = ble_hs_id_infer_auto(0, &blehr_addr_type);
+ assert(rc == 0);
+
+ /* Begin advertising */
+ blehr_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();
+
+ /* Initialize the NimBLE host configuration */
+ ble_hs_cfg.sync_cb = blehr_on_sync;
+
+ os_callout_init(&blehr_tx_timer, os_eventq_dflt_get(),
+ blehr_tx_hrate, NULL);
+
+ rc = gatt_svr_init();
+ assert(rc == 0);
+
+ /* Set the default device name */
+ rc = ble_svc_gap_device_name_set(device_name);
+ assert(rc == 0);
+
+ /* As the last thing, process events from default event queue */
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+ return 0;
+}
+
diff --git a/src/libs/mynewt-nimble/apps/blehr/syscfg.yml b/src/libs/mynewt-nimble/apps/blehr/syscfg.yml
new file mode 100644
index 00000000..a6be2e29
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blehr/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.vals:
+ # 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
+
+ # Log reboot messages to a flash circular buffer.
+ REBOOT_LOG_FCB: 1
+ LOG_FCB: 1
+ CONFIG_FCB: 1
+
+ # Set public device address.
+ BLE_PUBLIC_DEV_ADDR: ((uint8_t[6]){0xcc, 0xbb, 0xaa, 0x33, 0x22, 0x11})
diff --git a/src/libs/mynewt-nimble/apps/blemesh/pkg.yml b/src/libs/mynewt-nimble/apps/blemesh/pkg.yml
new file mode 100644
index 00000000..21cbb3d1
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh/pkg.yml
@@ -0,0 +1,37 @@
+# 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/blemesh
+pkg.type: app
+pkg.description: Sample application for BLE Mesh node with on/off model
+pkg.author: "Łukasz Rymanowski <lukasz.rymanowski@codecoup.pl>"
+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"
+ - "@apache-mynewt-core/sys/shell"
+ - nimble/controller
+ - nimble/host
+ - nimble/host/services/gap
+ - nimble/host/services/gatt
+ - nimble/host/store/ram
+ - nimble/transport/ram
diff --git a/src/libs/mynewt-nimble/apps/blemesh/src/main.c b/src/libs/mynewt-nimble/apps/blemesh/src/main.c
new file mode 100644
index 00000000..24c9950e
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh/src/main.c
@@ -0,0 +1,468 @@
+/*
+ * 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 "os/mynewt.h"
+#include "mesh/mesh.h"
+#include "console/console.h"
+#include "hal/hal_system.h"
+#include "hal/hal_gpio.h"
+#include "bsp/bsp.h"
+#include "shell/shell.h"
+
+/* BLE */
+#include "nimble/ble.h"
+#include "host/ble_hs.h"
+#include "services/gap/ble_svc_gap.h"
+#include "mesh/glue.h"
+
+/* Company ID */
+#define CID_VENDOR 0x05C3
+#define STANDARD_TEST_ID 0x00
+#define TEST_ID 0x01
+static int recent_test_id = STANDARD_TEST_ID;
+
+#define FAULT_ARR_SIZE 2
+
+static bool has_reg_fault = true;
+
+static struct bt_mesh_cfg_srv cfg_srv = {
+ .relay = BT_MESH_RELAY_DISABLED,
+ .beacon = BT_MESH_BEACON_ENABLED,
+#if MYNEWT_VAL(BLE_MESH_FRIEND)
+ .frnd = BT_MESH_FRIEND_ENABLED,
+#else
+ .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
+#endif
+#if MYNEWT_VAL(BLE_MESH_GATT_PROXY)
+ .gatt_proxy = BT_MESH_GATT_PROXY_ENABLED,
+#else
+ .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
+#endif
+ .default_ttl = 7,
+
+ /* 3 transmissions with 20ms interval */
+ .net_transmit = BT_MESH_TRANSMIT(2, 20),
+ .relay_retransmit = BT_MESH_TRANSMIT(2, 20),
+};
+
+static int
+fault_get_cur(struct bt_mesh_model *model,
+ uint8_t *test_id,
+ uint16_t *company_id,
+ uint8_t *faults,
+ uint8_t *fault_count)
+{
+ uint8_t reg_faults[FAULT_ARR_SIZE] = { [0 ... FAULT_ARR_SIZE-1] = 0xff };
+
+ console_printf("fault_get_cur() has_reg_fault %u\n", has_reg_fault);
+
+ *test_id = recent_test_id;
+ *company_id = CID_VENDOR;
+
+ *fault_count = min(*fault_count, sizeof(reg_faults));
+ memcpy(faults, reg_faults, *fault_count);
+
+ return 0;
+}
+
+static int
+fault_get_reg(struct bt_mesh_model *model,
+ uint16_t company_id,
+ uint8_t *test_id,
+ uint8_t *faults,
+ uint8_t *fault_count)
+{
+ if (company_id != CID_VENDOR) {
+ return -BLE_HS_EINVAL;
+ }
+
+ console_printf("fault_get_reg() has_reg_fault %u\n", has_reg_fault);
+
+ *test_id = recent_test_id;
+
+ if (has_reg_fault) {
+ uint8_t reg_faults[FAULT_ARR_SIZE] = { [0 ... FAULT_ARR_SIZE-1] = 0xff };
+
+ *fault_count = min(*fault_count, sizeof(reg_faults));
+ memcpy(faults, reg_faults, *fault_count);
+ } else {
+ *fault_count = 0;
+ }
+
+ return 0;
+}
+
+static int
+fault_clear(struct bt_mesh_model *model, uint16_t company_id)
+{
+ if (company_id != CID_VENDOR) {
+ return -BLE_HS_EINVAL;
+ }
+
+ has_reg_fault = false;
+
+ return 0;
+}
+
+static int
+fault_test(struct bt_mesh_model *model, uint8_t test_id, uint16_t company_id)
+{
+ if (company_id != CID_VENDOR) {
+ return -BLE_HS_EINVAL;
+ }
+
+ if (test_id != STANDARD_TEST_ID && test_id != TEST_ID) {
+ return -BLE_HS_EINVAL;
+ }
+
+ recent_test_id = test_id;
+ has_reg_fault = true;
+ bt_mesh_fault_update(bt_mesh_model_elem(model));
+
+ return 0;
+}
+
+static const struct bt_mesh_health_srv_cb health_srv_cb = {
+ .fault_get_cur = &fault_get_cur,
+ .fault_get_reg = &fault_get_reg,
+ .fault_clear = &fault_clear,
+ .fault_test = &fault_test,
+};
+
+static struct bt_mesh_health_srv health_srv = {
+ .cb = &health_srv_cb,
+};
+
+static struct bt_mesh_model_pub health_pub;
+
+static void
+health_pub_init(void)
+{
+ health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(0);
+}
+
+static struct bt_mesh_model_pub gen_level_pub;
+static struct bt_mesh_model_pub gen_onoff_pub;
+
+static uint8_t gen_on_off_state;
+static int16_t gen_level_state;
+
+static void gen_onoff_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(3);
+ uint8_t *status;
+
+ console_printf("#mesh-onoff STATUS\n");
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x04));
+ status = net_buf_simple_add(msg, 1);
+ *status = gen_on_off_state;
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ console_printf("#mesh-onoff STATUS: send status failed\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void gen_onoff_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ console_printf("#mesh-onoff GET\n");
+
+ gen_onoff_status(model, ctx);
+}
+
+static void gen_onoff_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ console_printf("#mesh-onoff SET\n");
+
+ gen_on_off_state = buf->om_data[0];
+ hal_gpio_write(LED_2, !gen_on_off_state);
+
+ gen_onoff_status(model, ctx);
+}
+
+static void gen_onoff_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ console_printf("#mesh-onoff SET-UNACK\n");
+
+ gen_on_off_state = buf->om_data[0];
+ hal_gpio_write(LED_2, !gen_on_off_state);
+}
+
+static const struct bt_mesh_model_op gen_onoff_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x01), 0, gen_onoff_get },
+ { BT_MESH_MODEL_OP_2(0x82, 0x02), 2, gen_onoff_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x03), 2, gen_onoff_set_unack },
+ BT_MESH_MODEL_OP_END,
+};
+
+static void gen_level_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(4);
+
+ console_printf("#mesh-level STATUS\n");
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x08));
+ net_buf_simple_add_le16(msg, gen_level_state);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ console_printf("#mesh-level STATUS: send status failed\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void gen_level_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ console_printf("#mesh-level GET\n");
+
+ gen_level_status(model, ctx);
+}
+
+static void gen_level_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ int16_t level;
+
+ level = (int16_t) net_buf_simple_pull_le16(buf);
+ console_printf("#mesh-level SET: level=%d\n", level);
+
+ gen_level_status(model, ctx);
+
+ gen_level_state = level;
+ console_printf("#mesh-level: level=%d\n", gen_level_state);
+}
+
+static void gen_level_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ int16_t level;
+
+ level = (int16_t) net_buf_simple_pull_le16(buf);
+ console_printf("#mesh-level SET-UNACK: level=%d\n", level);
+
+ gen_level_state = level;
+ console_printf("#mesh-level: level=%d\n", gen_level_state);
+}
+
+static void gen_delta_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ int16_t delta_level;
+
+ delta_level = (int16_t) net_buf_simple_pull_le16(buf);
+ console_printf("#mesh-level DELTA-SET: delta_level=%d\n", delta_level);
+
+ gen_level_status(model, ctx);
+
+ gen_level_state += delta_level;
+ console_printf("#mesh-level: level=%d\n", gen_level_state);
+}
+
+static void gen_delta_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ int16_t delta_level;
+
+ delta_level = (int16_t) net_buf_simple_pull_le16(buf);
+ console_printf("#mesh-level DELTA-SET: delta_level=%d\n", delta_level);
+
+ gen_level_state += delta_level;
+ console_printf("#mesh-level: level=%d\n", gen_level_state);
+}
+
+static void gen_move_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+}
+
+static void gen_move_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+}
+
+static const struct bt_mesh_model_op gen_level_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x05), 0, gen_level_get },
+ { BT_MESH_MODEL_OP_2(0x82, 0x06), 3, gen_level_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x07), 3, gen_level_set_unack },
+ { BT_MESH_MODEL_OP_2(0x82, 0x09), 5, gen_delta_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x0a), 5, gen_delta_set_unack },
+ { BT_MESH_MODEL_OP_2(0x82, 0x0b), 3, gen_move_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x0c), 3, gen_move_set_unack },
+ BT_MESH_MODEL_OP_END,
+};
+
+static struct bt_mesh_model root_models[] = {
+ BT_MESH_MODEL_CFG_SRV(&cfg_srv),
+ BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_op,
+ &gen_onoff_pub, NULL),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, gen_level_op,
+ &gen_level_pub, NULL),
+};
+
+static struct bt_mesh_model_pub vnd_model_pub;
+
+static void vnd_model_recv(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(3);
+
+ console_printf("#vendor-model-recv\n");
+
+ console_printf("data:%s len:%d\n", bt_hex(buf->om_data, buf->om_len),
+ buf->om_len);
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_3(0x01, CID_VENDOR));
+ os_mbuf_append(msg, buf->om_data, buf->om_len);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ console_printf("#vendor-model-recv: send rsp failed\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static const struct bt_mesh_model_op vnd_model_op[] = {
+ { BT_MESH_MODEL_OP_3(0x01, CID_VENDOR), 0, vnd_model_recv },
+ BT_MESH_MODEL_OP_END,
+};
+
+static struct bt_mesh_model vnd_models[] = {
+ BT_MESH_MODEL_VND(CID_VENDOR, BT_MESH_MODEL_ID_GEN_ONOFF_SRV, vnd_model_op,
+ &vnd_model_pub, NULL),
+};
+
+static struct bt_mesh_elem elements[] = {
+ BT_MESH_ELEM(0, root_models, vnd_models),
+};
+
+static const struct bt_mesh_comp comp = {
+ .cid = CID_VENDOR,
+ .elem = elements,
+ .elem_count = ARRAY_SIZE(elements),
+};
+
+static int output_number(bt_mesh_output_action_t action, uint32_t number)
+{
+ console_printf("OOB Number: %lu\n", number);
+
+ return 0;
+}
+
+static void prov_complete(u16_t net_idx, u16_t addr)
+{
+ console_printf("Local node provisioned, primary address 0x%04x\n", addr);
+}
+
+static const uint8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID);
+
+static const struct bt_mesh_prov prov = {
+ .uuid = dev_uuid,
+ .output_size = 4,
+ .output_actions = BT_MESH_DISPLAY_NUMBER | BT_MESH_BEEP | BT_MESH_VIBRATE | BT_MESH_BLINK,
+ .output_number = output_number,
+ .complete = prov_complete,
+};
+
+static void
+blemesh_on_reset(int reason)
+{
+ BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
+}
+
+static void
+blemesh_on_sync(void)
+{
+ int err;
+ ble_addr_t addr;
+
+ console_printf("Bluetooth initialized\n");
+
+ /* Use NRPA */
+ err = ble_hs_id_gen_rnd(1, &addr);
+ assert(err == 0);
+ err = ble_hs_id_set_rnd(addr.val);
+ assert(err == 0);
+
+ err = bt_mesh_init(addr.type, &prov, &comp);
+ if (err) {
+ console_printf("Initializing mesh failed (err %d)\n", err);
+ return;
+ }
+
+#if (MYNEWT_VAL(BLE_MESH_SHELL))
+ shell_register_default_module("mesh");
+#endif
+
+ console_printf("Mesh initialized\n");
+
+ if (IS_ENABLED(CONFIG_SETTINGS)) {
+ settings_load();
+ }
+
+ if (bt_mesh_is_provisioned()) {
+ printk("Mesh network restored from flash\n");
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+
+#ifdef ARCH_sim
+ mcu_sim_parse_args(argc, argv);
+#endif
+
+ /* Initialize OS */
+ sysinit();
+
+ /* Initialize the NimBLE host configuration. */
+ ble_hs_cfg.reset_cb = blemesh_on_reset;
+ ble_hs_cfg.sync_cb = blemesh_on_sync;
+ ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
+
+ hal_gpio_init_out(LED_2, 0);
+
+ health_pub_init();
+
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/blemesh/syscfg.yml b/src/libs/mynewt-nimble/apps/blemesh/syscfg.yml
new file mode 100644
index 00000000..383f1923
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh/syscfg.yml
@@ -0,0 +1,39 @@
+# 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:
+ # Enable the shell task.
+ SHELL_TASK: 1
+
+ # Set log level to info (disable debug logging).
+ LOG_LEVEL: 1
+
+ # Default task settings
+ OS_MAIN_STACK_SIZE: 768
+
+ # MCUmgr SMP is not supported in this app, so disable smp-over-shell.
+ SHELL_MGMT: 0
+
+ BLE_MESH: 1
+ MSYS_1_BLOCK_COUNT: 48
+
+ BLE_MESH_ADV_BUF_COUNT: 20
+ BLE_MESH_TX_SEG_MAX: 6
+
+ BLE_MESH_SETTINGS: 0
+ CONFIG_NFFS: 0
diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/pkg.yml b/src/libs/mynewt-nimble/apps/blemesh_light/pkg.yml
new file mode 100644
index 00000000..9694f18b
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_light/pkg.yml
@@ -0,0 +1,37 @@
+# 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/blemesh_light
+pkg.type: app
+pkg.description: Sample application for BLE Mesh node with Light model
+pkg.author: "Michał Narajowski <michal.narajowski@codecoup.pl>"
+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"
+ - "@apache-mynewt-core/sys/shell"
+ - nimble/controller
+ - nimble/host
+ - nimble/host/services/gap
+ - nimble/host/services/gatt
+ - nimble/host/store/ram
+ - nimble/transport/ram
diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.c b/src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.c
new file mode 100644
index 00000000..8b00e2c0
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.c
@@ -0,0 +1,242 @@
+/*
+ * 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 "syscfg/syscfg.h"
+
+#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
+
+#include "mesh/mesh.h"
+#include "bsp.h"
+#include "pwm/pwm.h"
+#include "light_model.h"
+#include "ws2812.h"
+
+#if (!MYNEWT_VAL(USE_NEOPIXEL))
+#if MYNEWT_VAL(PWM_0)
+struct pwm_dev *pwm0;
+#endif
+#if MYNEWT_VAL(PWM_1)
+struct pwm_dev *pwm1;
+#endif
+#if MYNEWT_VAL(PWM_2)
+struct pwm_dev *pwm2;
+#endif
+#if MYNEWT_VAL(PWM_3)
+struct pwm_dev *pwm3;
+#endif
+
+static uint16_t top_val;
+#endif
+
+#if (MYNEWT_VAL(USE_NEOPIXEL))
+static uint32_t neopixel[WS2812_NUM_LED];
+#endif
+
+static u8_t gen_onoff_state;
+static s16_t gen_level_state;
+
+static void light_set_lightness(u8_t percentage)
+{
+#if (!MYNEWT_VAL(USE_NEOPIXEL))
+ int rc;
+
+ uint16_t pwm_val = (uint16_t) (percentage * top_val / 100);
+
+#if MYNEWT_VAL(PWM_0)
+ rc = pwm_set_duty_cycle(pwm0, 0, pwm_val);
+ assert(rc == 0);
+#endif
+#if MYNEWT_VAL(PWM_1)
+ rc = pwm_set_duty_cycle(pwm1, 0, pwm_val);
+ assert(rc == 0);
+#endif
+#if MYNEWT_VAL(PWM_2)
+ rc = pwm_set_duty_cycle(pwm2, 0, pwm_val);
+ assert(rc == 0);
+#endif
+#if MYNEWT_VAL(PWM_3)
+ rc = pwm_set_duty_cycle(pwm3, 0, pwm_val);
+ assert(rc == 0);
+#endif
+#else
+ int i;
+ u32_t lightness;
+ u8_t max_lightness = 0x1f;
+
+ lightness = (u8_t) (percentage * max_lightness / 100);
+
+ for (i = 0; i < WS2812_NUM_LED; i++) {
+ neopixel[i] = (lightness | lightness << 8 | lightness << 16);
+ }
+ ws2812_write(neopixel);
+#endif
+}
+
+static void update_light_state(void)
+{
+ u16_t level = (u16_t)gen_level_state;
+ int percent = 100 * level / 0xffff;
+
+ if (gen_onoff_state == 0) {
+ percent = 0;
+ }
+ light_set_lightness((uint8_t) percent);
+}
+
+int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state)
+{
+ *state = gen_onoff_state;
+ return 0;
+}
+
+int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state)
+{
+ gen_onoff_state = state;
+ update_light_state();
+ return 0;
+}
+
+int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level)
+{
+ *level = gen_level_state;
+ return 0;
+}
+
+int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level)
+{
+ gen_level_state = level;
+ if ((u16_t)gen_level_state > 0x0000) {
+ gen_onoff_state = 1;
+ }
+ if ((u16_t)gen_level_state == 0x0000) {
+ gen_onoff_state = 0;
+ }
+ update_light_state();
+ return 0;
+}
+
+int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness)
+{
+ return light_model_gen_level_get(model, lightness);
+}
+
+int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness)
+{
+ return light_model_gen_level_set(model, lightness);
+}
+
+#if (!MYNEWT_VAL(USE_NEOPIXEL))
+struct pwm_dev_cfg dev_conf = {
+ .n_cycles = 0,
+ .int_prio = 3,
+};
+
+#if MYNEWT_VAL(PWM_0)
+static struct pwm_chan_cfg led1_conf = {
+ .pin = LED_1,
+ .inverted = true,
+};
+#endif
+
+#if MYNEWT_VAL(PWM_1)
+static struct pwm_chan_cfg led2_conf = {
+ .pin = LED_2,
+ .inverted = true,
+};
+#endif
+
+#if MYNEWT_VAL(PWM_2)
+static struct pwm_chan_cfg led3_conf = {
+ .pin = LED_3,
+ .inverted = true,
+};
+#endif
+#endif
+
+#if MYNEWT_VAL(PWM_3)
+static struct pwm_chan_cfg led4_conf = {
+ .pin = LED_4,
+ .inverted = true,
+};
+#endif
+
+#if (!MYNEWT_VAL(USE_NEOPIXEL))
+void init_pwm_dev(struct pwm_dev **pwm, char *dev_name, struct pwm_chan_cfg *chan_cfg)
+{
+ int rc = 0;
+
+ *pwm = (struct pwm_dev *) os_dev_open(dev_name, 0, NULL);
+ assert(pwm);
+ rc = pwm_configure_device(*pwm, &dev_conf);
+ assert(rc == 0);
+ rc = pwm_configure_channel(*pwm, 0, chan_cfg);
+ assert(rc == 0);
+ rc = pwm_enable(*pwm);
+ assert(rc == 0);
+}
+
+int pwm_init(void)
+{
+
+#if MYNEWT_VAL(PWM_0)
+ init_pwm_dev(&pwm0, "pwm0", &led1_conf);
+#endif
+
+#if MYNEWT_VAL(PWM_1)
+ init_pwm_dev(&pwm1, "pwm1", &led2_conf);
+#endif
+
+#if MYNEWT_VAL(PWM_2)
+ init_pwm_dev(&pwm2, "pwm2", &led3_conf);
+#endif
+
+#if MYNEWT_VAL(PWM_3)
+ init_pwm_dev(&pwm3, "pwm3", &led4_conf);
+#endif
+
+ if (!pwm0) {
+ return 0;
+ }
+
+ top_val = (uint16_t) pwm_get_top_value(pwm0);
+ update_light_state();
+
+ return 0;
+}
+#endif
+#endif
+
+int light_model_init(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
+ int rc;
+#if (!MYNEWT_VAL(USE_NEOPIXEL))
+ rc = pwm_init();
+ assert(rc == 0);
+#else
+ rc = ws2812_init();
+ assert(rc == 0);
+ update_light_state();
+#endif
+ return rc;
+#else
+ return 0;
+#endif
+}
+
diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.h b/src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.h
new file mode 100644
index 00000000..7fcdd0c3
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ *
+ * Copyright (c) 2017 Intel Corporation
+ *
+ */
+
+#ifndef __BT_MESH_LIGHT_MODEL_H
+#define __BT_MESH_LIGHT_MODEL_H
+
+#include "syscfg/syscfg.h"
+#include "mesh/mesh.h"
+
+int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state);
+int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state);
+int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level);
+int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level);
+int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness);
+int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness);
+int light_model_init(void);
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/src/main.c b/src/libs/mynewt-nimble/apps/blemesh_light/src/main.c
new file mode 100644
index 00000000..51d86eb5
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_light/src/main.c
@@ -0,0 +1,113 @@
+/*
+ * 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 "sysinit/sysinit.h"
+#include "os/os.h"
+#include "mesh/mesh.h"
+#include "console/console.h"
+#include "bsp/bsp.h"
+#include "shell/shell.h"
+
+#include "host/ble_hs.h"
+#include "mesh/glue.h"
+#include "mesh/testing.h"
+#include "mesh/model_srv.h"
+#include "light_model.h"
+
+
+static void model_bound_cb(u16_t addr, struct bt_mesh_model *model,
+ u16_t key_idx)
+{
+ int rc;
+
+ console_printf("Model bound: remote addr 0x%04x key_idx 0x%04x model %p\n",
+ addr, key_idx, model);
+
+ if (model->id != BT_MESH_MODEL_ID_GEN_LEVEL_SRV) {
+ return;
+ }
+
+ /* Hack for demo purposes */
+ rc = bt_test_bind_app_key_to_model(model, key_idx,
+ BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV);
+
+ if (rc) {
+ console_printf("Failed to bind light lightness srv model to app_key");
+ } else {
+ console_printf("Successfuly bound light lightness srv model to app_key");
+ }
+}
+
+static struct bt_test_cb bt_test_cb = {
+ .mesh_model_bound = model_bound_cb,
+};
+
+static void
+blemesh_on_reset(int reason)
+{
+ BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
+}
+
+static void
+blemesh_on_sync(void)
+{
+ console_printf("Bluetooth initialized\n");
+
+ shell_register_default_module("mesh");
+
+ bt_test_cb_register(&bt_test_cb);
+
+ light_model_init();
+ bt_mesh_set_gen_onoff_srv_cb(light_model_gen_onoff_get,
+ light_model_gen_onoff_set);
+ bt_mesh_set_gen_level_srv_cb(light_model_gen_level_get,
+ light_model_gen_level_set);
+ bt_mesh_set_light_lightness_srv_cb(light_model_light_lightness_get,
+ light_model_light_lightness_set);
+
+ console_printf("Mesh initialized\n");
+
+ if (IS_ENABLED(CONFIG_SETTINGS)) {
+ settings_load();
+ }
+
+ if (bt_mesh_is_provisioned()) {
+ printk("Mesh network restored from flash\n");
+ }
+
+ /* Hack for demo purposes */
+ bt_test_shell_init();
+}
+
+int
+main(void)
+{
+ /* Initialize OS */
+ sysinit();
+
+ /* Initialize the NimBLE host configuration. */
+ ble_hs_cfg.reset_cb = blemesh_on_reset;
+ ble_hs_cfg.sync_cb = blemesh_on_sync;
+ ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
+
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.c b/src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.c
new file mode 100644
index 00000000..f27a182b
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.c
@@ -0,0 +1,138 @@
+/**
+ * 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 "syscfg/syscfg.h"
+
+#if (MYNEWT_VAL(USE_NEOPIXEL))
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include "sysinit/sysinit.h"
+#include "os/os.h"
+#include "bsp/bsp.h"
+#include "pwm/pwm.h"
+#include "nrfx.h"
+#include "nrfx_pwm.h"
+#include "ws2812.h"
+
+#define BITS_PER_SEQ (24)
+#define BIT0 (0x8000 | 6)
+#define BIT1 (0x8000 | 11)
+
+static const nrfx_pwm_t pwm = NRFX_PWM_INSTANCE(WS2812_PWM);
+
+static const nrfx_pwm_config_t pwm_config = {
+ .output_pins = { WS2812_GPIO, NRFX_PWM_PIN_NOT_USED, NRFX_PWM_PIN_NOT_USED, NRFX_PWM_PIN_NOT_USED },
+ .irq_priority = 3,
+ .base_clock = NRF_PWM_CLK_16MHz,
+ .count_mode = NRF_PWM_MODE_UP,
+ .top_value = 20,
+ .load_mode = NRF_PWM_LOAD_COMMON,
+ .step_mode = NRF_PWM_STEP_AUTO,
+};
+
+static uint16_t pwm_seq_values[2][BITS_PER_SEQ];
+
+static const nrf_pwm_sequence_t pwm_seq[2] = {
+ {
+ .values.p_raw = pwm_seq_values[0],
+ .length = BITS_PER_SEQ,
+ .repeats = 0,
+ .end_delay = 0,
+ }, {
+ .values.p_raw = pwm_seq_values[1],
+ .length = BITS_PER_SEQ,
+ .repeats = 0,
+ .end_delay = 0,
+ },
+};
+
+static uint32_t led_color[WS2812_NUM_LED];
+static int led_idx;
+
+static void
+load_pixel(void)
+{
+ uint16_t *seq_values;
+ uint32_t grb;
+ int i;
+
+ seq_values = pwm_seq_values[led_idx & 1];
+ grb = led_color[led_idx];
+
+ for (i = 0; i < BITS_PER_SEQ; i++) {
+ *seq_values = grb & 0x800000 ? BIT1 : BIT0;
+ grb <<= 1;
+ seq_values++;
+ }
+
+ led_idx++;
+}
+
+static void
+pwm_handler_func(nrfx_pwm_evt_type_t event_type)
+{
+ switch (event_type) {
+ case NRFX_PWM_EVT_END_SEQ0:
+ case NRFX_PWM_EVT_END_SEQ1:
+ load_pixel();
+ break;
+ default:
+ break;
+ }
+}
+
+int
+ws2812_init(void)
+{
+ nrfx_err_t err;
+
+ err = nrfx_pwm_init(&pwm, &pwm_config, pwm_handler_func);
+
+ return err != NRFX_SUCCESS;
+}
+
+int
+ws2812_write(const uint32_t *rgb)
+{
+ uint32_t grb;
+ int i;
+
+ for (i = 0; i < WS2812_NUM_LED; i++) {
+ grb = 0;
+ grb |= (rgb[i] & 0x00FF00) << 8;
+ grb |= (rgb[i] & 0xFF0000) >> 8;
+ grb |= (rgb[i] & 0x0000FF);
+
+ led_color[i] = grb;
+ }
+
+ led_idx = 0;
+
+ load_pixel();
+ load_pixel();
+ nrfx_pwm_complex_playback(&pwm, &pwm_seq[0], &pwm_seq[1], WS2812_NUM_LED,
+ NRFX_PWM_FLAG_SIGNAL_END_SEQ0 |
+ NRFX_PWM_FLAG_SIGNAL_END_SEQ1 |
+ NRFX_PWM_FLAG_STOP);
+
+ return 0;
+}
+#endif
diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.h b/src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.h
new file mode 100644
index 00000000..93b8faf8
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.h
@@ -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.
+ */
+
+#ifndef __WS2812_H__
+#define __WS2812_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WS2812_PWM 0
+#define WS2812_GPIO 30
+#define WS2812_NUM_LED 32
+
+int ws2812_init(void);
+
+int ws2812_write(const uint32_t *rgb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __WS2812_H__ */
diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/syscfg.yml b/src/libs/mynewt-nimble/apps/blemesh_light/syscfg.yml
new file mode 100644
index 00000000..82c70dc2
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_light/syscfg.yml
@@ -0,0 +1,63 @@
+# 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:
+ USE_NEOPIXEL:
+ value: 0
+
+syscfg.vals:
+ # Enable the shell task.
+ SHELL_TASK: 1
+
+ # Set log level to info (disable debug logging).
+ LOG_LEVEL: 1
+
+ # Default task settings
+ OS_MAIN_STACK_SIZE: 768
+
+ # SMP is not supported in this app, so disable smp-over-shell.
+ SHELL_MGMT: 0
+
+ MSYS_1_BLOCK_COUNT: 80
+
+ BLE_MESH_ADV_BUF_COUNT: 20
+ BLE_MESH_TX_SEG_MAX: 6
+
+ BLE_MESH: 1
+ BLE_MESH_SHELL: 1
+ BLE_MESH_PROV: 1
+ BLE_MESH_PB_ADV: 1
+ BLE_MESH_PB_GATT: 1
+ BLE_MESH_GATT_PROXY: 1
+ BLE_MESH_TESTING: 1
+ BLE_MESH_FRIEND: 0
+ BLE_MESH_CFG_CLI: 1
+ BLE_MESH_HEALTH_CLI: 0
+ BLE_MESH_SHELL_MODELS: 1
+ BLE_MESH_OOB_OUTPUT_ACTIONS: 0
+ BLE_MESH_SETTINGS: 0
+ CONFIG_NFFS: 0
+
+ USE_NEOPIXEL: 0
+
+syscfg.vals.BLE_MESH_SHELL_MODELS:
+ PWM_0: 1
+ PWM_1: 1
+ PWM_2: 1
+ PWM_3: 1
+
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_1/README.md b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/README.md
new file mode 100644
index 00000000..fde49536
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/README.md
@@ -0,0 +1,79 @@
+### Bluetooth: Mesh OnOff Model
+
+
+#### Overview
+
+This is a simple application demonstrating a Bluetooth mesh multi-element node.
+Each element has a mesh onoff client and server
+model which controls one of the 4 sets of buttons and LEDs .
+
+Prior to provisioning, an unprovisioned beacon is broadcast that contains
+a UUID. Each button controls the state of its
+corresponding LED and does not initiate any mesh activity.
+
+The models for button 1 and LED 1 are in the node's root element.
+The 3 remaining button/LED pairs are in elements 1 through 3.
+Assuming the provisioner assigns 0x100 to the root element,
+the secondary elements will appear at 0x101, 0x102 and 0x103.
+
+After provisioning, the button clients must
+be configured to publish and the LED servers to subscribe.
+
+If a LED server is provided with a publish address, it will
+also publish its status on an onoff state change.
+
+If a button is pressed only once within a 1 second interval, it sends an
+"on" message. If it is pressed more than once, it
+sends an "off" message. The buttons are quite noisy and are debounced in
+the button_pressed() interrupt handler. An interrupt within 250ms of the
+previous is discarded. This might seem a little clumsy, but the alternative of
+using one button for "on" and another for "off" would reduce the number
+of onoff clients from 4 to 2.
+
+#### Requirements
+************
+
+This sample has been tested on the Nordic nRF52840-PDK board, but would
+likely also run on the nrf52_pca10040 board.
+
+#### Building and Running
+********************
+
+Prior to provisioning, each button controls its corresponding LED as one
+would expect with an actual switch.
+
+Provisioning is done using the BlueZ meshctl utility. Below is an example that
+binds button 2 and LED 1 to application key 1. It then configures button 2
+to publish to group 0xc000 and LED 1 to subscribe to that group.
+
+```
+discover-unprovisioned on
+provision <discovered UUID>
+menu config
+target 0100
+appkey-add 1
+bind 0 1 1000 # bind appkey 1 to LED server on element 0 (unicast 0100)
+sub-add 0100 c000 1000 # add subscription to group address c000 to the LED server
+bind 1 1 1001 # bind appkey 1 to button 2 on element 1 (unicast 0101)
+pub-set 0101 c000 1 0 0 1001 # publish button 2 to group address c000
+```
+
+The meshctl utility maintains a persistent JSON database containing
+the mesh configuration. As additional nodes (boards) are provisioned, it
+assigns sequential unicast addresses based on the number of elements
+supported by the node. This example supports 4 elements per node.
+
+The first or root element of the node contains models for configuration,
+health, and onoff. The secondary elements only
+have models for onoff. The meshctl target for configuration must be the
+root element's unicast address as it is the only one that has a
+configuration server model.
+
+If meshctl is gracefully exited, it can be restarted and reconnected to
+network 0x0.
+
+The meshctl utility also supports a onoff model client that can be used to
+change the state of any LED that is bound to application key 0x1.
+This is done by setting the target to the unicast address of the element
+that has that LED's model and issuing the onoff command.
+Group addresses are not supported.
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_1/pkg.yml b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/pkg.yml
new file mode 100644
index 00000000..451f37a1
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/pkg.yml
@@ -0,0 +1,34 @@
+# 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/blemesh_models_example_1
+pkg.type: app
+pkg.description: Sample application for BLE Mesh node with on/off model on nRF52840pdk
+pkg.author: "Michał Narajowski <michal.narajowski@codecoup.pl>"
+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/stats/full"
+ - nimble/controller
+ - nimble/host
+ - nimble/host/services/gap
+ - nimble/host/services/gatt
+ - nimble/transport/ram
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_1/src/main.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/src/main.c
new file mode 100644
index 00000000..ef398c9f
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/src/main.c
@@ -0,0 +1,709 @@
+/* main.c - Application main entry point */
+
+/*
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/*
+ * This application is specific to the Nordic nRF52840-PDK board.
+ *
+ * It supports the 4 buttons and 4 LEDs as mesh clients and servers.
+ *
+ * Prior to provisioning, a button inverts the state of the
+ * corresponding LED.
+ *
+ * Button and LED 1 are in the root node.
+ * The 3 remaining button/LED pairs are in element 1 through 3.
+ * Assuming the provisioner assigns 0x100 to the root node,
+ * the secondary elements will appear at 0x101, 0x102 and 0x103.
+ *
+ * It's anticipated that after provisioning, the button clients would
+ * be configured to publish and the LED servers to subscribe.
+ *
+ * If a LED server is provided with a publish address, it will
+ * also publish its status on a state change.
+ *
+ * Messages from a button to its corresponding LED are ignored as
+ * the LED's state has already been changed locally by the button client.
+ *
+ * The buttons are debounced at a nominal 250ms. That value can be
+ * changed as needed.
+ *
+ */
+
+#include "os/mynewt.h"
+#include "bsp/bsp.h"
+#include "console/console.h"
+#include "hal/hal_gpio.h"
+#include "host/ble_hs.h"
+#include "mesh/glue.h"
+#include "mesh/mesh.h"
+
+#define CID_RUNTIME 0x05C3
+
+/* Model Operation Codes */
+#define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01)
+#define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02)
+#define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03)
+#define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04)
+
+static void gen_onoff_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf);
+
+static void gen_onoff_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf);
+
+static void gen_onoff_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf);
+
+static void gen_onoff_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf);
+
+/*
+ * Server Configuration Declaration
+ */
+
+static struct bt_mesh_cfg_srv cfg_srv = {
+ .relay = BT_MESH_RELAY_DISABLED,
+ .beacon = BT_MESH_BEACON_ENABLED,
+#if defined(CONFIG_BT_MESH_FRIEND)
+ .frnd = BT_MESH_FRIEND_ENABLED,
+#else
+ .frnd = BT_MESH_FRIEND_NOT_SUPPORTED,
+#endif
+#if defined(CONFIG_BT_MESH_GATT_PROXY)
+ .gatt_proxy = BT_MESH_GATT_PROXY_ENABLED,
+#else
+ .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
+#endif
+ .default_ttl = 7,
+
+ /* 3 transmissions with 20ms interval */
+ .net_transmit = BT_MESH_TRANSMIT(2, 20),
+ .relay_retransmit = BT_MESH_TRANSMIT(2, 20),
+};
+
+/*
+ * Client Configuration Declaration
+ */
+
+static struct bt_mesh_cfg_cli cfg_cli = {
+};
+
+/*
+ * Health Server Declaration
+ */
+
+static struct bt_mesh_health_srv health_srv = {
+};
+
+/*
+ * Publication Declarations
+ *
+ * The publication messages are initialized to the
+ * the size of the opcode + content
+ *
+ * For publication, the message must be in static or global as
+ * it is re-transmitted several times. This occurs
+ * after the function that called bt_mesh_model_publish() has
+ * exited and the stack is no longer valid.
+ *
+ * Note that the additional 4 bytes for the AppMIC is not needed
+ * because it is added to a stack variable at the time a
+ * transmission occurs.
+ *
+ */
+
+static struct bt_mesh_model_pub health_pub;
+static struct bt_mesh_model_pub gen_onoff_pub_srv;
+static struct bt_mesh_model_pub gen_onoff_pub_cli;
+static struct bt_mesh_model_pub gen_onoff_pub_srv_s_0;
+static struct bt_mesh_model_pub gen_onoff_pub_cli_s_0;
+static struct bt_mesh_model_pub gen_onoff_pub_srv_s_1;
+static struct bt_mesh_model_pub gen_onoff_pub_cli_s_1;
+static struct bt_mesh_model_pub gen_onoff_pub_srv_s_2;
+static struct bt_mesh_model_pub gen_onoff_pub_cli_s_2;
+
+static struct os_mbuf *bt_mesh_pub_msg_health_pub;
+static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv;
+static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli;
+static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_0;
+static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_0;
+static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_1;
+static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_1;
+static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_2;
+static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_2;
+
+void init_pub(void)
+{
+ bt_mesh_pub_msg_health_pub = NET_BUF_SIMPLE(1 + 3 + 0);
+ bt_mesh_pub_msg_gen_onoff_pub_srv = NET_BUF_SIMPLE(2 + 2);
+ bt_mesh_pub_msg_gen_onoff_pub_cli = NET_BUF_SIMPLE(2 + 2);
+ bt_mesh_pub_msg_gen_onoff_pub_srv_s_0 = NET_BUF_SIMPLE(2 + 2);
+ bt_mesh_pub_msg_gen_onoff_pub_cli_s_0 = NET_BUF_SIMPLE(2 + 2);
+ bt_mesh_pub_msg_gen_onoff_pub_srv_s_1 = NET_BUF_SIMPLE(2 + 2);
+ bt_mesh_pub_msg_gen_onoff_pub_cli_s_1 = NET_BUF_SIMPLE(2 + 2);
+ bt_mesh_pub_msg_gen_onoff_pub_srv_s_2 = NET_BUF_SIMPLE(2 + 2);
+ bt_mesh_pub_msg_gen_onoff_pub_cli_s_2 = NET_BUF_SIMPLE(2 + 2);
+
+ health_pub.msg = bt_mesh_pub_msg_health_pub;
+ gen_onoff_pub_srv.msg = bt_mesh_pub_msg_gen_onoff_pub_srv;
+ gen_onoff_pub_cli.msg = bt_mesh_pub_msg_gen_onoff_pub_cli;
+ gen_onoff_pub_srv_s_0.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_0;
+ gen_onoff_pub_cli_s_0.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_0;
+ gen_onoff_pub_srv_s_1.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_1;
+ gen_onoff_pub_cli_s_1.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_1;
+ gen_onoff_pub_srv_s_2.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_2;
+ gen_onoff_pub_cli_s_2.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_2;
+}
+
+/*
+ * Models in an element must have unique op codes.
+ *
+ * The mesh stack dispatches a message to the first model in an element
+ * that is also bound to an app key and supports the op code in the
+ * received message.
+ *
+ */
+
+/*
+ * OnOff Model Server Op Dispatch Table
+ *
+ */
+
+static const struct bt_mesh_model_op gen_onoff_srv_op[] = {
+ { BT_MESH_MODEL_OP_GEN_ONOFF_GET, 0, gen_onoff_get },
+ { BT_MESH_MODEL_OP_GEN_ONOFF_SET, 2, gen_onoff_set },
+ { BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2, gen_onoff_set_unack },
+ BT_MESH_MODEL_OP_END,
+};
+
+/*
+ * OnOff Model Client Op Dispatch Table
+ */
+
+static const struct bt_mesh_model_op gen_onoff_cli_op[] = {
+ { BT_MESH_MODEL_OP_GEN_ONOFF_STATUS, 1, gen_onoff_status },
+ BT_MESH_MODEL_OP_END,
+};
+
+struct onoff_state {
+ u8_t current;
+ u8_t previous;
+ u8_t led_gpio_pin;
+};
+
+/*
+ * Declare and Initialize Element Contexts
+ * Change to select different GPIO output pins
+ */
+
+static struct onoff_state onoff_state_arr[] = {
+ { .led_gpio_pin = LED_1 },
+ { .led_gpio_pin = LED_2 },
+ { .led_gpio_pin = LED_3 },
+ { .led_gpio_pin = LED_4 },
+};
+
+/*
+ *
+ * Element Model Declarations
+ *
+ * Element 0 Root Models
+ */
+
+static struct bt_mesh_model root_models[] = {
+ BT_MESH_MODEL_CFG_SRV(&cfg_srv),
+ BT_MESH_MODEL_CFG_CLI(&cfg_cli),
+ BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
+ &gen_onoff_pub_srv, &onoff_state_arr[0]),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
+ &gen_onoff_pub_cli, &onoff_state_arr[0]),
+};
+
+/*
+ * Element 1 Models
+ */
+
+static struct bt_mesh_model secondary_0_models[] = {
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
+ &gen_onoff_pub_srv_s_0, &onoff_state_arr[1]),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
+ &gen_onoff_pub_cli_s_0, &onoff_state_arr[1]),
+};
+
+/*
+ * Element 2 Models
+ */
+
+static struct bt_mesh_model secondary_1_models[] = {
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
+ &gen_onoff_pub_srv_s_1, &onoff_state_arr[2]),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
+ &gen_onoff_pub_cli_s_1, &onoff_state_arr[2]),
+};
+
+/*
+ * Element 3 Models
+ */
+
+static struct bt_mesh_model secondary_2_models[] = {
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
+ &gen_onoff_pub_srv_s_2, &onoff_state_arr[3]),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
+ &gen_onoff_pub_cli_s_2, &onoff_state_arr[3]),
+};
+
+/*
+ * Button to Client Model Assignments
+ */
+
+struct bt_mesh_model *mod_cli_sw[] = {
+ &root_models[4],
+ &secondary_0_models[1],
+ &secondary_1_models[1],
+ &secondary_2_models[1],
+};
+
+/*
+ * LED to Server Model Assigmnents
+ */
+
+struct bt_mesh_model *mod_srv_sw[] = {
+ &root_models[3],
+ &secondary_0_models[0],
+ &secondary_1_models[0],
+ &secondary_2_models[0],
+};
+
+/*
+ * Root and Secondary Element Declarations
+ */
+
+static struct bt_mesh_elem elements[] = {
+ BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE),
+ BT_MESH_ELEM(0, secondary_0_models, BT_MESH_MODEL_NONE),
+ BT_MESH_ELEM(0, secondary_1_models, BT_MESH_MODEL_NONE),
+ BT_MESH_ELEM(0, secondary_2_models, BT_MESH_MODEL_NONE),
+};
+
+static const struct bt_mesh_comp comp = {
+ .cid = CID_RUNTIME,
+ .elem = elements,
+ .elem_count = ARRAY_SIZE(elements),
+};
+
+struct sw {
+ u8_t sw_num;
+ u8_t onoff_state;
+ struct os_callout button_work;
+ struct os_callout button_timer;
+};
+
+
+static u8_t button_press_cnt;
+static struct sw sw;
+
+static u8_t trans_id;
+static u32_t time, last_time;
+static u16_t primary_addr;
+static u16_t primary_net_idx;
+
+/*
+ * Generic OnOff Model Server Message Handlers
+ *
+ * Mesh Model Specification 3.1.1
+ *
+ */
+
+static void gen_onoff_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4);
+ struct onoff_state *state = model->user_data;
+
+ BT_INFO("addr 0x%04x onoff 0x%02x",
+ bt_mesh_model_elem(model)->addr, state->current);
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
+ net_buf_simple_add_u8(msg, state->current);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send On Off Status response");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void gen_onoff_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = model->pub->msg;
+ struct onoff_state *state = model->user_data;
+ int err;
+
+ state->current = net_buf_simple_pull_u8(buf);
+ BT_INFO("addr 0x%02x state 0x%02x",
+ bt_mesh_model_elem(model)->addr, state->current);
+
+ /* Pin set low turns on LED's on the nrf52840-pca10056 board */
+ hal_gpio_write(state->led_gpio_pin,
+ state->current ? 0 : 1);
+
+ /*
+ * If a server has a publish address, it is required to
+ * publish status on a state change
+ *
+ * See Mesh Profile Specification 3.7.6.1.2
+ *
+ * Only publish if there is an assigned address
+ */
+
+ if (state->previous != state->current &&
+ model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+ BT_INFO("publish last 0x%02x cur 0x%02x",
+ state->previous,
+ state->current);
+ state->previous = state->current;
+ bt_mesh_model_msg_init(msg,
+ BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
+ net_buf_simple_add_u8(msg, state->current);
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ BT_ERR("bt_mesh_model_publish err %d", err);
+ }
+ }
+}
+
+static void gen_onoff_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ BT_INFO("");
+
+ gen_onoff_set_unack(model, ctx, buf);
+ gen_onoff_get(model, ctx, buf);
+}
+
+static void gen_onoff_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t state;
+
+ state = net_buf_simple_pull_u8(buf);
+
+ BT_INFO("Node 0x%04x OnOff status from 0x%04x with state 0x%02x",
+ bt_mesh_model_elem(model)->addr, ctx->addr, state);
+}
+
+static int output_number(bt_mesh_output_action_t action, u32_t number)
+{
+ BT_INFO("OOB Number %u", number);
+ return 0;
+}
+
+static int output_string(const char *str)
+{
+ BT_INFO("OOB String %s", str);
+ return 0;
+}
+
+static void prov_complete(u16_t net_idx, u16_t addr)
+{
+ BT_INFO("provisioning complete for net_idx 0x%04x addr 0x%04x",
+ net_idx, addr);
+ primary_addr = addr;
+ primary_net_idx = net_idx;
+}
+
+static void prov_reset(void)
+{
+ bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
+}
+
+static u8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID);
+
+#define BUTTON_DEBOUNCE_DELAY_MS 250
+
+/*
+ * Map GPIO pins to button number
+ * Change to select different GPIO input pins
+ */
+
+static uint8_t pin_to_sw(int pin_pos)
+{
+ switch (pin_pos) {
+ case BUTTON_1: return 0;
+ case BUTTON_2: return 1;
+ case BUTTON_3: return 2;
+ case BUTTON_4: return 3;
+ default:break;
+ }
+
+ BT_ERR("No match for GPIO pin 0x%08x", pin_pos);
+ return 0;
+}
+
+static void button_pressed(struct os_event *ev)
+{
+ int pin_pos = (int ) ev->ev_arg;
+ /*
+ * One button press within a 1 second interval sends an on message
+ * More than one button press sends an off message
+ */
+
+ time = k_uptime_get_32();
+
+ /* debounce the switch */
+ if (time < last_time + BUTTON_DEBOUNCE_DELAY_MS) {
+ last_time = time;
+ return;
+ }
+
+ if (button_press_cnt == 0) {
+ os_callout_reset(&sw.button_timer, os_time_ms_to_ticks32(K_SECONDS(1)));
+ }
+
+ BT_INFO("button_press_cnt 0x%02x", button_press_cnt);
+ button_press_cnt++;
+
+ /* The variable pin_pos is the pin position in the GPIO register,
+ * not the pin number. It's assumed that only one bit is set.
+ */
+
+ sw.sw_num = pin_to_sw(pin_pos);
+ last_time = time;
+}
+
+/*
+ * Button Count Timer Worker
+ */
+
+static void button_cnt_timer(struct os_event *work)
+{
+ struct sw *button_sw = work->ev_arg;
+
+ button_sw->onoff_state = button_press_cnt == 1 ? 1 : 0;
+ BT_INFO("button_press_cnt 0x%02x onoff_state 0x%02x",
+ button_press_cnt, button_sw->onoff_state);
+ button_press_cnt = 0;
+ os_callout_reset(&sw.button_work, 0);
+}
+
+/*
+ * Button Pressed Worker Task
+ */
+
+static void button_pressed_worker(struct os_event *work)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(1);
+ struct bt_mesh_model *mod_cli, *mod_srv;
+ struct bt_mesh_model_pub *pub_cli, *pub_srv;
+ struct sw *sw = work->ev_arg;
+ u8_t sw_idx = sw->sw_num;
+ int err;
+
+ mod_cli = mod_cli_sw[sw_idx];
+ pub_cli = mod_cli->pub;
+
+ mod_srv = mod_srv_sw[sw_idx];
+ pub_srv = mod_srv->pub;
+ (void)pub_srv;
+
+ /* If unprovisioned, just call the set function.
+ * The intent is to have switch-like behavior
+ * prior to provisioning. Once provisioned,
+ * the button and its corresponding led are no longer
+ * associated and act independently. So, if a button is to
+ * control its associated led after provisioning, the button
+ * must be configured to either publish to the led's unicast
+ * address or a group to which the led is subscribed.
+ */
+
+ if (primary_addr == BT_MESH_ADDR_UNASSIGNED) {
+ struct bt_mesh_msg_ctx ctx = {
+ .addr = sw_idx + primary_addr,
+ };
+
+ /* This is a dummy message sufficient
+ * for the led server
+ */
+
+ net_buf_simple_add_u8(msg, sw->onoff_state);
+ gen_onoff_set_unack(mod_srv, &ctx, msg);
+ goto done;
+ }
+
+ if (pub_cli->addr == BT_MESH_ADDR_UNASSIGNED) {
+ goto done;
+ }
+
+ BT_INFO("publish to 0x%04x onoff 0x%04x sw_idx 0x%04x",
+ pub_cli->addr, sw->onoff_state, sw_idx);
+ bt_mesh_model_msg_init(pub_cli->msg,
+ BT_MESH_MODEL_OP_GEN_ONOFF_SET);
+ net_buf_simple_add_u8(pub_cli->msg, sw->onoff_state);
+ net_buf_simple_add_u8(pub_cli->msg, trans_id++);
+ err = bt_mesh_model_publish(mod_cli);
+ if (err) {
+ BT_ERR("bt_mesh_model_publish err %d", err);
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+}
+
+/* Disable OOB security for SILabs Android app */
+
+static const struct bt_mesh_prov prov = {
+ .uuid = dev_uuid,
+#if 1
+ .output_size = 6,
+ .output_actions = (BT_MESH_DISPLAY_NUMBER | BT_MESH_DISPLAY_STRING),
+ .output_number = output_number,
+ .output_string = output_string,
+#else
+.output_size = 0,
+ .output_actions = 0,
+ .output_number = 0,
+#endif
+ .complete = prov_complete,
+ .reset = prov_reset,
+};
+
+void init_led(u8_t dev)
+{
+ hal_gpio_init_out(onoff_state_arr[dev].led_gpio_pin, 1);
+}
+
+static struct os_event button_event;
+
+static void
+gpio_irq_handler(void *arg)
+{
+ button_event.ev_arg = arg;
+ os_eventq_put(os_eventq_dflt_get(), &button_event);
+}
+
+void init_button(int button)
+{
+ button_event.ev_cb = button_pressed;
+
+ hal_gpio_irq_init(button, gpio_irq_handler, (void *)button,
+ HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
+ hal_gpio_irq_enable(button);
+}
+
+static void
+blemesh_on_reset(int reason)
+{
+ BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
+}
+
+static void
+blemesh_on_sync(void)
+{
+ int err;
+ ble_addr_t addr;
+
+ console_printf("Bluetooth initialized\n");
+
+ /* Use NRPA */
+ err = ble_hs_id_gen_rnd(1, &addr);
+ assert(err == 0);
+ err = ble_hs_id_set_rnd(addr.val);
+ assert(err == 0);
+
+ err = bt_mesh_init(addr.type, &prov, &comp);
+ if (err) {
+ console_printf("Initializing mesh failed (err %d)\n", err);
+ return;
+ }
+
+ if (IS_ENABLED(CONFIG_SETTINGS)) {
+ settings_load();
+ }
+
+ if (bt_mesh_is_provisioned()) {
+ console_printf("Mesh network restored from flash\n");
+ }
+
+ bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV);
+
+ console_printf("Mesh initialized\n");
+}
+
+int main(void)
+{
+#ifdef ARCH_sim
+ mcu_sim_parse_args(argc, argv);
+#endif
+
+ /* Initialize OS */
+ sysinit();
+
+ BT_INFO("Initializing...");
+
+ /* Initialize the button debouncer */
+ last_time = k_uptime_get_32();
+
+ /* Initialize button worker task*/
+ os_callout_init(&sw.button_work, os_eventq_dflt_get(),
+ button_pressed_worker, &sw);
+
+ /* Initialize button count timer */
+ os_callout_init(&sw.button_timer, os_eventq_dflt_get(),
+ button_cnt_timer, &sw);
+
+ /* Initialize LED's */
+ init_led(0);
+ init_led(1);
+ init_led(2);
+ init_led(3);
+
+ init_button(BUTTON_1);
+ init_button(BUTTON_2);
+ init_button(BUTTON_3);
+ init_button(BUTTON_4);
+
+ init_pub();
+
+ /* Initialize the NimBLE host configuration. */
+ ble_hs_cfg.reset_cb = blemesh_on_reset;
+ ble_hs_cfg.sync_cb = blemesh_on_sync;
+ ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
+
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_1/syscfg.yml b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/syscfg.yml
new file mode 100644
index 00000000..969753fe
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/syscfg.yml
@@ -0,0 +1,38 @@
+# 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:
+ # Set log level to info (disable debug logging).
+ LOG_LEVEL: 1
+
+ # Default task settings
+ OS_MAIN_STACK_SIZE: 768
+
+ # SMP is not supported in this app, so disable smp-over-shell.
+ SHELL_MGMT: 0
+
+ MSYS_1_BLOCK_COUNT: 48
+
+ BLE_MESH: 1
+ BLE_MESH_CFG_CLI: 1
+ BLE_MESH_DEV_UUID: "((uint8_t[16]){0xdd, 0xdd, 0})"
+ BLE_MESH_SETTINGS: 0
+ CONFIG_NFFS: 0
+
+ BLE_MESH_ADV_BUF_COUNT: 20
+ BLE_MESH_TX_SEG_MAX: 6
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/README.md b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/README.md
new file mode 100644
index 00000000..7bec909e
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/README.md
@@ -0,0 +1,101 @@
+#### Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+
+##### Overview
+********
+
+This is a application demonstrating a Bluetooth mesh node in
+which Root element has following models
+
+- Generic OnOff Server
+- Generic OnOff Client
+- Generic Level Server
+- Generic Level Client
+- Generic Default Transition Time Server
+- Generic Default Transition Time Client
+- Generic Power OnOff Server
+- Generic Power OnOff Setup Server
+- Generic Power OnOff Client
+- Light Lightness Server
+- Light Lightness Setup Server
+- Light Lightness Client
+- Light CTL Server
+- Light CTL Setup Server
+- Light CTL Client
+- Vendor Model
+
+And Secondary element has following models
+
+- Generic Level Server
+- Generic Level Client
+- Light CTL Temperature Server
+
+Prior to provisioning, an unprovisioned beacon is broadcast that contains
+a unique UUID. Each button controls the state of its
+corresponding LED and does not initiate any mesh activity
+
+##### Associations of Models with hardware
+************************************
+For the nRF52840-PDK board, these are the model associations:
+
+* LED1 is associated with generic OnOff Server's state which is part of Root element
+* LED2 is associated with Vendor Model which is part of Root element
+* LED3 is associated with generic Level (ROOT) / Light Lightness Actual value
+* LED4 is associated with generic Level (Secondary) / Light CTL Temperature value
+* Button1 and Button2 are associated with gen. OnOff Client or Vendor Model which is part of Root element
+* Button3 and Button4 are associated with gen. Level Client / Light Lightness Client / Light CTL Client which is part of Root element
+
+States of Servers are bounded as per Bluetooth SIG Mesh Model Specification v1.0
+
+After provisioning, the button clients must
+be configured to publish and the LED servers to subscribe.
+If a server is provided with a publish address, it will
+also publish its relevant status.
+
+##### Requirements
+************
+This sample has been tested on the Nordic nRF52840-PDK board, but would
+likely also run on the nrf52_pca10040 board.
+
+
+##### Running
+************
+
+Provisioning is done using the BlueZ meshctl utility. In this example, we'll use meshctl commands to bind:
+
+- Button1, Button2, and LED1 to application key 1. It then configures Button1 and Button2
+ to publish to group 0xC000 and LED1 to subscribe to that group.
+- Button3, Button4, and LED3 to application key 1. It then configures Button3 and Button4
+ to publish to group 0xC000 and LED3 to subscribe to that group.
+
+```
+discover-unprovisioned on
+provision <discovered UUID>
+menu config
+target 0100
+appkey-add 1
+bind 0 1 1000
+bind 0 1 1001
+bind 0 1 1002
+bind 0 1 1003
+sub-add 0100 c000 1000
+sub-add 0100 c000 1002
+pub-set 0100 c000 1 0 5 1001
+pub-set 0100 c000 1 0 5 1003
+```
+
+The meshctl utility maintains a persistent JSON database containing
+the mesh configuration. As additional nodes (boards) are provisioned, it
+assigns sequential unicast addresses based on the number of elements
+supported by the node. This example supports 2 elements per node.
+
+The meshctl target for configuration must be the root element's unicast
+address as it is the only one that has a configuration server model. If
+meshctl is gracefully exited, it can be restarted and reconnected to
+network 0x0.
+
+The meshctl utility also supports a onoff model client that can be used to
+change the state of any LED that is bound to application key 0x1.
+This is done by setting the target to the unicast address of the element
+that has that LED's model and issuing the onoff command.
+Group addresses are not supported.
+
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/pkg.yml b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/pkg.yml
new file mode 100644
index 00000000..ee2be6da
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/pkg.yml
@@ -0,0 +1,40 @@
+# 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/blemesh_models_example_2
+pkg.type: app
+pkg.description: Sample application for BLE Mesh node with on/off, level, light and vendor models on nRF52840pdk
+pkg.author: "Michał Narajowski <michal.narajowski@codecoup.pl>"
+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/stats/full"
+ - "@apache-mynewt-core/encoding/base64"
+ - "@apache-mynewt-core/sys/config"
+ - nimble/controller
+ - nimble/host
+ - nimble/host/services/gap
+ - nimble/host/services/gatt
+ - nimble/transport/ram
+
+pkg.lflags:
+ - -DFLOAT_SUPPORT
+ - -lm
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.c
new file mode 100644
index 00000000..76e361af
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.c
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "bsp/bsp.h"
+#include "console/console.h"
+#include "hal/hal_gpio.h"
+#include "mesh/mesh.h"
+
+#include "app_gpio.h"
+#include "publisher.h"
+
+int button_device[] = {
+ BUTTON_1,
+ BUTTON_2,
+ BUTTON_3,
+ BUTTON_4,
+};
+
+int led_device[] = {
+ LED_1,
+ LED_2,
+ LED_3,
+ LED_4,
+};
+
+static struct os_callout button_work;
+
+static void button_pressed(struct os_event *ev)
+{
+ os_callout_reset(&button_work, 0);
+}
+
+static struct os_event button_event;
+
+static void gpio_irq_handler(void *arg)
+{
+ button_event.ev_arg = arg;
+ os_eventq_put(os_eventq_dflt_get(), &button_event);
+}
+
+void app_gpio_init(void)
+{
+ /* LEDs configiuratin & setting */
+
+ hal_gpio_init_out(led_device[0], 1);
+ hal_gpio_init_out(led_device[1], 1);
+ hal_gpio_init_out(led_device[2], 1);
+ hal_gpio_init_out(led_device[3], 1);
+
+ /* Buttons configiuratin & setting */
+
+ os_callout_init(&button_work, os_eventq_dflt_get(), publish, NULL);
+
+ button_event.ev_cb = button_pressed;
+
+ hal_gpio_irq_init(button_device[0], gpio_irq_handler, NULL,
+ HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
+ hal_gpio_irq_enable(button_device[0]);
+
+ hal_gpio_irq_init(button_device[1], gpio_irq_handler, NULL,
+ HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
+ hal_gpio_irq_enable(button_device[1]);
+
+ hal_gpio_irq_init(button_device[2], gpio_irq_handler, NULL,
+ HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
+ hal_gpio_irq_enable(button_device[2]);
+
+ hal_gpio_irq_init(button_device[3], gpio_irq_handler, NULL,
+ HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
+ hal_gpio_irq_enable(button_device[3]);
+}
+
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.h
new file mode 100644
index 00000000..6eabc1ce
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.h
@@ -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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _APP_GPIO_H
+#define _APP_GPIO_H
+
+/* GPIO */
+extern int button_device[];
+extern int led_device[];
+
+void app_gpio_init(void);
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.c
new file mode 100644
index 00000000..86d4c515
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.c
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "console/console.h"
+
+#include "common.h"
+#include "ble_mesh.h"
+#include "device_composition.h"
+
+#define OOB_AUTH_ENABLE 1
+
+#ifdef OOB_AUTH_ENABLE
+
+static int output_number(bt_mesh_output_action_t action, u32_t number)
+{
+ printk("OOB Number: %lu\n", number);
+ return 0;
+}
+
+static int output_string(const char *str)
+{
+ printk("OOB String: %s\n", str);
+ return 0;
+}
+
+#endif
+
+static void prov_complete(u16_t net_idx, u16_t addr)
+{
+ printk("Local node provisioned, primary address 0x%04x\n", addr);
+}
+
+static void prov_reset(void)
+{
+ bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
+}
+
+static u8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID);
+
+static const struct bt_mesh_prov prov = {
+ .uuid = dev_uuid,
+
+#ifdef OOB_AUTH_ENABLE
+
+ .output_size = 6,
+ .output_actions = BT_MESH_DISPLAY_NUMBER | BT_MESH_DISPLAY_STRING,
+ .output_number = output_number,
+ .output_string = output_string,
+
+#endif
+
+ .complete = prov_complete,
+ .reset = prov_reset,
+};
+
+void blemesh_on_reset(int reason)
+{
+ BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
+}
+
+void blemesh_on_sync(void)
+{
+ int err;
+ ble_addr_t addr;
+
+ console_printf("Bluetooth initialized\n");
+
+ /* Use NRPA */
+ err = ble_hs_id_gen_rnd(1, &addr);
+ assert(err == 0);
+ err = ble_hs_id_set_rnd(addr.val);
+ assert(err == 0);
+
+ err = bt_mesh_init(addr.type, &prov, &comp);
+ if (err) {
+ console_printf("Initializing mesh failed (err %d)\n", err);
+ return;
+ }
+
+ if (IS_ENABLED(CONFIG_SETTINGS)) {
+ settings_load();
+ }
+
+ if (bt_mesh_is_provisioned()) {
+ console_printf("Mesh network restored from flash\n");
+ }
+
+ bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV);
+
+ console_printf("Mesh initialized\n");
+
+ bt_initialized();
+}
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.h
new file mode 100644
index 00000000..c02af243
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _BLE_MESH_H
+#define _BLE_MESH_H
+
+#include "mesh/mesh.h"
+#include "mesh/glue.h"
+
+/* Model Operation Codes */
+#define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01)
+#define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02)
+#define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03)
+#define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04)
+
+#define BT_MESH_MODEL_OP_GEN_LEVEL_GET BT_MESH_MODEL_OP_2(0x82, 0x05)
+#define BT_MESH_MODEL_OP_GEN_LEVEL_SET BT_MESH_MODEL_OP_2(0x82, 0x06)
+#define BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x07)
+#define BT_MESH_MODEL_OP_GEN_LEVEL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x08)
+#define BT_MESH_MODEL_OP_GEN_DELTA_SET BT_MESH_MODEL_OP_2(0x82, 0x09)
+#define BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0A)
+#define BT_MESH_MODEL_OP_GEN_MOVE_SET BT_MESH_MODEL_OP_2(0x82, 0x0B)
+#define BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0C)
+
+#define BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS BT_MESH_MODEL_OP_2(0x82, 0x10)
+
+#define BT_MESH_MODEL_GEN_ONPOWERUP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x12)
+
+#define BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS BT_MESH_MODEL_OP_2(0x82, 0x4E)
+#define BT_MESH_MODEL_LIGHT_LIGHTNESS_LINEAR_STATUS \
+ BT_MESH_MODEL_OP_2(0x82, 0x52)
+#define BT_MESH_MODEL_LIGHT_LIGHTNESS_LAST_STATUS \
+ BT_MESH_MODEL_OP_2(0x82, 0x54)
+#define BT_MESH_MODEL_LIGHT_LIGHTNESS_DEFAULT_STATUS \
+ BT_MESH_MODEL_OP_2(0x82, 0x56)
+#define BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS \
+ BT_MESH_MODEL_OP_2(0x82, 0x58)
+
+#define BT_MESH_MODEL_LIGHT_CTL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x60)
+#define BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS \
+ BT_MESH_MODEL_OP_2(0x82, 0x63)
+#define BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x66)
+#define BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS BT_MESH_MODEL_OP_2(0x82, 0x68)
+
+void blemesh_on_reset(int reason);
+void blemesh_on_sync(void);
+void init_pub(void);
+
+#endif
+
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/common.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/common.h
new file mode 100644
index 00000000..57cd401a
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/common.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+void update_light_state(void);
+void bt_initialized(void);
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.c
new file mode 100644
index 00000000..b638b861
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.c
@@ -0,0 +1,2779 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "console/console.h"
+#include "hal/hal_gpio.h"
+#include "mesh/mesh.h"
+
+#include "app_gpio.h"
+#include "storage.h"
+
+#include "ble_mesh.h"
+#include "device_composition.h"
+#include "state_binding.h"
+#include "transition.h"
+
+static struct bt_mesh_cfg_srv cfg_srv = {
+ .relay = BT_MESH_RELAY_ENABLED,
+ .beacon = BT_MESH_BEACON_ENABLED,
+
+#if defined(CONFIG_BT_MESH_FRIEND)
+ .frnd = BT_MESH_FRIEND_ENABLED,
+#else
+ .frnd = BT_MESH_FRIEND_NOT_SUPPORTED,
+#endif
+
+#if defined(CONFIG_BT_MESH_GATT_PROXY)
+ .gatt_proxy = BT_MESH_GATT_PROXY_ENABLED,
+#else
+ .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
+#endif
+
+ .default_ttl = 7,
+
+ /* 2 transmissions with 20ms interval */
+ .net_transmit = BT_MESH_TRANSMIT(2, 20),
+
+ /* 3 transmissions with 20ms interval */
+ .relay_retransmit = BT_MESH_TRANSMIT(3, 20),
+};
+
+static struct bt_mesh_health_srv health_srv = {
+};
+
+static struct bt_mesh_model_pub health_pub;
+
+static struct bt_mesh_model_pub gen_onoff_srv_pub_root;
+static struct bt_mesh_model_pub gen_onoff_cli_pub_root;
+static struct bt_mesh_model_pub gen_level_srv_pub_root;
+static struct bt_mesh_model_pub gen_level_cli_pub_root;
+static struct bt_mesh_model_pub gen_def_trans_time_srv_pub;
+static struct bt_mesh_model_pub gen_def_trans_time_cli_pub;
+static struct bt_mesh_model_pub gen_power_onoff_srv_pub;
+static struct bt_mesh_model_pub gen_power_onoff_cli_pub;
+static struct bt_mesh_model_pub light_lightness_srv_pub;
+static struct bt_mesh_model_pub light_lightness_cli_pub;
+static struct bt_mesh_model_pub light_ctl_srv_pub;
+static struct bt_mesh_model_pub light_ctl_cli_pub;
+static struct bt_mesh_model_pub vnd_pub;
+static struct bt_mesh_model_pub gen_level_srv_pub_s0;
+static struct bt_mesh_model_pub gen_level_cli_pub_s0;
+
+
+static struct os_mbuf *bt_mesh_pub_msg_health_pub;
+static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_srv_pub_root;
+static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_cli_pub_root;
+static struct os_mbuf *bt_mesh_pub_msg_gen_level_srv_pub_root;
+static struct os_mbuf *bt_mesh_pub_msg_gen_level_cli_pub_root;
+static struct os_mbuf *bt_mesh_pub_msg_gen_def_trans_time_srv_pub;
+static struct os_mbuf *bt_mesh_pub_msg_gen_def_trans_time_cli_pub;
+static struct os_mbuf *bt_mesh_pub_msg_gen_power_onoff_srv_pub;
+static struct os_mbuf *bt_mesh_pub_msg_gen_power_onoff_cli_pub;
+static struct os_mbuf *bt_mesh_pub_msg_light_lightness_srv_pub;
+static struct os_mbuf *bt_mesh_pub_msg_light_lightness_cli_pub;
+static struct os_mbuf *bt_mesh_pub_msg_light_ctl_srv_pub;
+static struct os_mbuf *bt_mesh_pub_msg_light_ctl_cli_pub;
+static struct os_mbuf *bt_mesh_pub_msg_vnd_pub;
+static struct os_mbuf *bt_mesh_pub_msg_gen_level_srv_pub_s0;
+static struct os_mbuf *bt_mesh_pub_msg_gen_level_cli_pub_s0;
+
+
+void init_pub(void)
+{
+ bt_mesh_pub_msg_health_pub = NET_BUF_SIMPLE(1 + 3 + 0);
+ bt_mesh_pub_msg_gen_onoff_srv_pub_root = NET_BUF_SIMPLE(2 + 3);
+ bt_mesh_pub_msg_gen_onoff_cli_pub_root = NET_BUF_SIMPLE(2 + 4);
+ bt_mesh_pub_msg_gen_level_srv_pub_root = NET_BUF_SIMPLE(2 + 5);
+ bt_mesh_pub_msg_gen_level_cli_pub_root = NET_BUF_SIMPLE(2 + 7);
+ bt_mesh_pub_msg_gen_power_onoff_srv_pub = NET_BUF_SIMPLE(2 + 1);
+ bt_mesh_pub_msg_gen_power_onoff_cli_pub = NET_BUF_SIMPLE(2 + 1);
+ bt_mesh_pub_msg_gen_def_trans_time_srv_pub = NET_BUF_SIMPLE(2 + 1);
+ bt_mesh_pub_msg_gen_def_trans_time_cli_pub = NET_BUF_SIMPLE(2 + 1);
+ bt_mesh_pub_msg_light_lightness_srv_pub = NET_BUF_SIMPLE(2 + 5);
+ bt_mesh_pub_msg_light_lightness_cli_pub = NET_BUF_SIMPLE(2 + 5);
+ bt_mesh_pub_msg_light_ctl_srv_pub = NET_BUF_SIMPLE(2 + 9);
+ bt_mesh_pub_msg_light_ctl_cli_pub = NET_BUF_SIMPLE(2 + 9);
+ bt_mesh_pub_msg_vnd_pub = NET_BUF_SIMPLE(3 + 6);
+ bt_mesh_pub_msg_gen_level_srv_pub_s0 = NET_BUF_SIMPLE(2 + 5);
+ bt_mesh_pub_msg_gen_level_cli_pub_s0 = NET_BUF_SIMPLE(2 + 7);
+
+
+ health_pub.msg = bt_mesh_pub_msg_health_pub;
+ gen_onoff_srv_pub_root.msg = bt_mesh_pub_msg_gen_onoff_srv_pub_root;
+ gen_onoff_cli_pub_root.msg = bt_mesh_pub_msg_gen_onoff_cli_pub_root;
+ gen_level_srv_pub_root.msg = bt_mesh_pub_msg_gen_level_srv_pub_root;
+ gen_level_cli_pub_root.msg = bt_mesh_pub_msg_gen_level_cli_pub_root;
+ gen_power_onoff_srv_pub.msg = bt_mesh_pub_msg_gen_power_onoff_srv_pub;
+ gen_power_onoff_cli_pub.msg = bt_mesh_pub_msg_gen_power_onoff_cli_pub;
+ gen_def_trans_time_srv_pub.msg = bt_mesh_pub_msg_gen_def_trans_time_srv_pub;
+ gen_def_trans_time_cli_pub.msg = bt_mesh_pub_msg_gen_def_trans_time_cli_pub;
+ light_lightness_srv_pub.msg = bt_mesh_pub_msg_light_lightness_srv_pub;
+ light_lightness_cli_pub.msg = bt_mesh_pub_msg_light_lightness_cli_pub;
+ light_ctl_srv_pub.msg = bt_mesh_pub_msg_light_ctl_srv_pub;
+ light_ctl_cli_pub.msg = bt_mesh_pub_msg_light_ctl_cli_pub;
+ vnd_pub.msg = bt_mesh_pub_msg_vnd_pub;
+ gen_level_srv_pub_s0.msg = bt_mesh_pub_msg_gen_level_srv_pub_s0;
+ gen_level_cli_pub_s0.msg = bt_mesh_pub_msg_gen_level_cli_pub_s0;
+}
+
+/* Definitions of models user data (Start) */
+struct generic_onoff_state gen_onoff_srv_root_user_data = {
+ .transition = &lightness_transition,
+};
+
+struct generic_level_state gen_level_srv_root_user_data = {
+ .transition = &lightness_transition,
+};
+
+struct gen_def_trans_time_state gen_def_trans_time_srv_user_data;
+
+struct generic_onpowerup_state gen_power_onoff_srv_user_data;
+
+struct light_lightness_state light_lightness_srv_user_data = {
+ .transition = &lightness_transition,
+};
+
+struct light_ctl_state light_ctl_srv_user_data = {
+ .transition = &lightness_transition,
+};
+
+struct vendor_state vnd_user_data;
+
+struct generic_level_state gen_level_srv_s0_user_data = {
+ .transition = &temp_transition,
+};
+/* Definitions of models user data (End) */
+
+static struct bt_mesh_elem elements[];
+
+/* message handlers (Start) */
+
+/* Generic OnOff Server message handlers */
+static void gen_onoff_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 3 + 4);
+ struct generic_onoff_state *state = model->user_data;
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
+ net_buf_simple_add_u8(msg, state->onoff);
+
+ if (state->transition->counter) {
+ calculate_rt(state->transition);
+ net_buf_simple_add_u8(msg, state->target_onoff);
+ net_buf_simple_add_u8(msg, state->transition->rt);
+ }
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send GEN_ONOFF_SRV Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+void gen_onoff_publish(struct bt_mesh_model *model)
+{
+ int err;
+ struct os_mbuf *msg = model->pub->msg;
+ struct generic_onoff_state *state = model->user_data;
+
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
+ net_buf_simple_add_u8(msg, state->onoff);
+
+ if (state->transition->counter) {
+ calculate_rt(state->transition);
+ net_buf_simple_add_u8(msg, state->target_onoff);
+ net_buf_simple_add_u8(msg, state->transition->rt);
+ }
+
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ printk("bt_mesh_model_publish err %d\n", err);
+ }
+}
+
+static void gen_onoff_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, onoff, tt, delay;
+ s64_t now;
+ struct generic_onoff_state *state = model->user_data;
+
+ onoff = net_buf_simple_pull_u8(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (onoff > STATE_ON) {
+ return;
+ }
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+ state->target_onoff = onoff;
+
+ if (state->target_onoff != state->onoff) {
+ onoff_tt_values(state, tt, delay);
+ } else {
+ gen_onoff_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->onoff = state->target_onoff;
+ }
+
+ state->transition->just_started = true;
+ gen_onoff_publish(model);
+ onoff_handler(state);
+}
+
+static void gen_onoff_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, onoff, tt, delay;
+ s64_t now;
+ struct generic_onoff_state *state = model->user_data;
+
+ onoff = net_buf_simple_pull_u8(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (onoff > STATE_ON) {
+ return;
+ }
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ gen_onoff_get(model, ctx, buf);
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+ state->target_onoff = onoff;
+
+ if (state->target_onoff != state->onoff) {
+ onoff_tt_values(state, tt, delay);
+ } else {
+ gen_onoff_get(model, ctx, buf);
+ gen_onoff_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->onoff = state->target_onoff;
+ }
+
+ state->transition->just_started = true;
+ gen_onoff_get(model, ctx, buf);
+ gen_onoff_publish(model);
+ onoff_handler(state);
+}
+
+/* Generic OnOff Client message handlers */
+static void gen_onoff_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from GEN_ONOFF_SRV\n");
+ printk("Present OnOff = %02x\n", net_buf_simple_pull_u8(buf));
+
+ if (buf->om_len == 2) {
+ printk("Target OnOff = %02x\n", net_buf_simple_pull_u8(buf));
+ printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf));
+ }
+}
+
+/* Generic Level Server message handlers */
+static void gen_level_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4);
+ struct generic_level_state *state = model->user_data;
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS);
+ net_buf_simple_add_le16(msg, state->level);
+
+ if (state->transition->counter) {
+ calculate_rt(state->transition);
+ net_buf_simple_add_le16(msg, state->target_level);
+ net_buf_simple_add_u8(msg, state->transition->rt);
+ }
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send GEN_LEVEL_SRV Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+void gen_level_publish(struct bt_mesh_model *model)
+{
+ int err;
+ struct os_mbuf *msg = model->pub->msg;
+ struct generic_level_state *state = model->user_data;
+
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS);
+ net_buf_simple_add_le16(msg, state->level);
+
+ if (state->transition->counter) {
+ calculate_rt(state->transition);
+ net_buf_simple_add_le16(msg, state->target_level);
+ net_buf_simple_add_u8(msg, state->transition->rt);
+ }
+
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ printk("bt_mesh_model_publish err %d\n", err);
+ }
+}
+
+static void gen_level_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ s16_t level;
+ s64_t now;
+ struct generic_level_state *state = model->user_data;
+
+ level = (s16_t) net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+ state->target_level = level;
+
+ if (state->target_level != state->level) {
+ level_tt_values(state, tt, delay);
+ } else {
+ gen_level_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->level = state->target_level;
+ }
+
+ state->transition->just_started = true;
+ gen_level_publish(model);
+
+ if (bt_mesh_model_elem(model)->addr == elements[0].addr) {
+ /* Root element */
+ transition_type = LEVEL_TT;
+ level_lightness_handler(state);
+ } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) {
+ /* Secondary element */
+ transition_type = LEVEL_TEMP_TT;
+ level_temp_handler(state);
+ }
+}
+
+static void gen_level_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ s16_t level;
+ s64_t now;
+ struct generic_level_state *state = model->user_data;
+
+ level = (s16_t) net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ gen_level_get(model, ctx, buf);
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+ state->target_level = level;
+
+ if (state->target_level != state->level) {
+ level_tt_values(state, tt, delay);
+ } else {
+ gen_level_get(model, ctx, buf);
+ gen_level_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->level = state->target_level;
+ }
+
+ state->transition->just_started = true;
+ gen_level_get(model, ctx, buf);
+ gen_level_publish(model);
+
+ if (bt_mesh_model_elem(model)->addr == elements[0].addr) {
+ /* Root element */
+ transition_type = LEVEL_TT;
+ level_lightness_handler(state);
+ } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) {
+ /* Secondary element */
+ transition_type = LEVEL_TEMP_TT;
+ level_temp_handler(state);
+ }
+}
+
+static void gen_delta_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ s32_t tmp32, delta;
+ s64_t now;
+ struct generic_level_state *state = model->user_data;
+
+ delta = (s32_t) net_buf_simple_pull_le32(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+
+ if (state->last_delta == delta) {
+ return;
+ }
+ tmp32 = state->last_level + delta;
+
+ } else {
+ state->last_level = state->level;
+ tmp32 = state->level + delta;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+ state->last_delta = delta;
+
+ if (tmp32 < INT16_MIN) {
+ tmp32 = INT16_MIN;
+ } else if (tmp32 > INT16_MAX) {
+ tmp32 = INT16_MAX;
+ }
+
+ state->target_level = tmp32;
+
+ if (state->target_level != state->level) {
+ level_tt_values(state, tt, delay);
+ } else {
+ gen_level_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->level = state->target_level;
+ }
+
+ state->transition->just_started = true;
+ gen_level_publish(model);
+
+ if (bt_mesh_model_elem(model)->addr == elements[0].addr) {
+ /* Root element */
+ transition_type = LEVEL_TT_DELTA;
+ level_lightness_handler(state);
+ } else if (bt_mesh_model_elem(model)->addr ==
+ elements[1].addr) {
+ /* Secondary element */
+ transition_type = LEVEL_TEMP_TT_DELTA;
+ level_temp_handler(state);
+ }
+}
+
+static void gen_delta_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ s32_t tmp32, delta;
+ s64_t now;
+ struct generic_level_state *state = model->user_data;
+
+ delta = (s32_t) net_buf_simple_pull_le32(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+
+ if (state->last_delta == delta) {
+ gen_level_get(model, ctx, buf);
+ return;
+ }
+ tmp32 = state->last_level + delta;
+
+ } else {
+ state->last_level = state->level;
+ tmp32 = state->level + delta;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+ state->last_delta = delta;
+
+ if (tmp32 < INT16_MIN) {
+ tmp32 = INT16_MIN;
+ } else if (tmp32 > INT16_MAX) {
+ tmp32 = INT16_MAX;
+ }
+
+ state->target_level = tmp32;
+
+ if (state->target_level != state->level) {
+ level_tt_values(state, tt, delay);
+ } else {
+ gen_level_get(model, ctx, buf);
+ gen_level_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->level = state->target_level;
+ }
+
+ state->transition->just_started = true;
+ gen_level_get(model, ctx, buf);
+ gen_level_publish(model);
+
+ if (bt_mesh_model_elem(model)->addr == elements[0].addr) {
+ /* Root element */
+ transition_type = LEVEL_TT_DELTA;
+ level_lightness_handler(state);
+ } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) {
+ /* Secondary element */
+ transition_type = LEVEL_TEMP_TT_DELTA;
+ level_temp_handler(state);
+ }
+}
+
+static void gen_level_move_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4);
+ struct generic_level_state *state = model->user_data;
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS);
+ net_buf_simple_add_le16(msg, state->level);
+
+ if (state->transition->counter) {
+ if (state->last_delta < 0) {
+ net_buf_simple_add_le16(msg, INT16_MIN);
+ } else { /* 0 should not be possible */
+ net_buf_simple_add_le16(msg, INT16_MAX);
+ }
+
+ net_buf_simple_add_u8(msg, UNKNOWN_VALUE);
+ }
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send GEN_LEVEL_SRV Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void gen_level_move_publish(struct bt_mesh_model *model)
+{
+ int err;
+ struct os_mbuf *msg = model->pub->msg;
+ struct generic_level_state *state = model->user_data;
+
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS);
+ net_buf_simple_add_le16(msg, state->level);
+
+ if (state->transition->counter) {
+ if (state->last_delta < 0) {
+ net_buf_simple_add_le16(msg, INT16_MIN);
+ } else { /* 0 should not be possible */
+ net_buf_simple_add_le16(msg, INT16_MAX);
+ }
+
+ net_buf_simple_add_u8(msg, UNKNOWN_VALUE);
+ }
+
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ printk("bt_mesh_model_publish err %d\n", err);
+ }
+}
+
+static void gen_move_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ s16_t delta;
+ s32_t tmp32;
+ s64_t now;
+ struct generic_level_state *state = model->user_data;
+
+ delta = (s16_t) net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+ state->last_delta = delta;
+
+ tmp32 = state->level + delta;
+ if (tmp32 < INT16_MIN) {
+ tmp32 = INT16_MIN;
+ } else if (tmp32 > INT16_MAX) {
+ tmp32 = INT16_MAX;
+ }
+
+ state->target_level = tmp32;
+
+ if (state->target_level != state->level) {
+ level_tt_values(state, tt, delay);
+ } else {
+ gen_level_move_publish(model);
+ return;
+ }
+
+ if (state->transition->counter == 0) {
+ return;
+ }
+
+ state->transition->just_started = true;
+ gen_level_move_publish(model);
+
+ if (bt_mesh_model_elem(model)->addr == elements[0].addr) {
+ /* Root element */
+ transition_type = LEVEL_TT_MOVE;
+ level_lightness_handler(state);
+ } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) {
+ /* Secondary element */
+ transition_type = LEVEL_TEMP_TT_MOVE;
+ level_temp_handler(state);
+ }
+}
+
+static void gen_move_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ s16_t delta;
+ s32_t tmp32;
+ s64_t now;
+ struct generic_level_state *state = model->user_data;
+
+ delta = (s16_t) net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ gen_level_move_get(model, ctx, buf);
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+ state->last_delta = delta;
+
+ tmp32 = state->level + delta;
+ if (tmp32 < INT16_MIN) {
+ tmp32 = INT16_MIN;
+ } else if (tmp32 > INT16_MAX) {
+ tmp32 = INT16_MAX;
+ }
+
+ state->target_level = tmp32;
+
+ if (state->target_level != state->level) {
+ level_tt_values(state, tt, delay);
+ } else {
+ gen_level_move_get(model, ctx, buf);
+ gen_level_move_publish(model);
+ return;
+ }
+
+ if (state->transition->counter == 0) {
+ return;
+ }
+
+ state->transition->just_started = true;
+ gen_level_move_get(model, ctx, buf);
+ gen_level_move_publish(model);
+
+ if (bt_mesh_model_elem(model)->addr == elements[0].addr) {
+ /* Root element */
+ transition_type = LEVEL_TT_MOVE;
+ level_lightness_handler(state);
+ } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) {
+ /* Secondary element */
+ transition_type = LEVEL_TEMP_TT_MOVE;
+ level_temp_handler(state);
+ }
+}
+
+/* Generic Level Client message handlers */
+static void gen_level_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from GEN_LEVEL_SRV\n");
+ printk("Present Level = %04x\n", net_buf_simple_pull_le16(buf));
+
+ if (buf->om_len == 3) {
+ printk("Target Level = %04x\n", net_buf_simple_pull_le16(buf));
+ printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf));
+ }
+}
+
+/* Generic Default Transition Time Server message handlers */
+static void gen_def_trans_time_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4);
+ struct gen_def_trans_time_state *state = model->user_data;
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS);
+ net_buf_simple_add_u8(msg, state->tt);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send GEN_DEF_TT_SRV Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void gen_def_trans_time_publish(struct bt_mesh_model *model)
+{
+ int err;
+ struct os_mbuf *msg = model->pub->msg;
+ struct gen_def_trans_time_state *state = model->user_data;
+
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS);
+ net_buf_simple_add_u8(msg, state->tt);
+
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ printk("bt_mesh_model_publish err %d\n", err);
+ }
+}
+
+static bool gen_def_trans_time_setunack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tt;
+ struct gen_def_trans_time_state *state = model->user_data;
+
+ tt = net_buf_simple_pull_u8(buf);
+
+ /* Here, Model specification is silent about tid implementation */
+
+ if ((tt & 0x3F) == 0x3F) {
+ return false;
+ }
+
+ if (state->tt != tt) {
+ state->tt = tt;
+ default_tt = tt;
+
+ save_on_flash(GEN_DEF_TRANS_TIME_STATE);
+ }
+
+ return true;
+}
+
+static void gen_def_trans_time_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ if (gen_def_trans_time_setunack(model, ctx, buf) == true) {
+ gen_def_trans_time_publish(model);
+ }
+}
+
+static void gen_def_trans_time_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ if (gen_def_trans_time_setunack(model, ctx, buf) == true) {
+ gen_def_trans_time_get(model, ctx, buf);
+ gen_def_trans_time_publish(model);
+ }
+}
+
+/* Generic Default Transition Time Client message handlers */
+static void gen_def_trans_time_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from GEN_DEF_TT_SRV\n");
+ printk("Transition Time = %02x\n", net_buf_simple_pull_u8(buf));
+}
+
+/* Generic Power OnOff Server message handlers */
+static void gen_onpowerup_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4);
+ struct generic_onpowerup_state *state = model->user_data;
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_ONPOWERUP_STATUS);
+ net_buf_simple_add_u8(msg, state->onpowerup);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send GEN_POWER_ONOFF_SRV Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+/* Generic Power OnOff Client message handlers */
+static void gen_onpowerup_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from GEN_POWER_ONOFF_SRV\n");
+ printk("OnPowerUp = %02x\n", net_buf_simple_pull_u8(buf));
+}
+
+/* Generic Power OnOff Setup Server message handlers */
+
+static void gen_onpowerup_publish(struct bt_mesh_model *model)
+{
+ int err;
+ struct os_mbuf *msg = model->pub->msg;
+ struct generic_onpowerup_state *state = model->user_data;
+
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_ONPOWERUP_STATUS);
+ net_buf_simple_add_u8(msg, state->onpowerup);
+
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ printk("bt_mesh_model_publish err %d\n", err);
+ }
+}
+
+static bool gen_onpowerup_setunack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t onpowerup;
+ struct generic_onpowerup_state *state = model->user_data;
+
+ onpowerup = net_buf_simple_pull_u8(buf);
+
+ /* Here, Model specification is silent about tid implementation */
+
+ if (onpowerup > STATE_RESTORE) {
+ return false;
+ }
+
+ if (state->onpowerup != onpowerup) {
+ state->onpowerup = onpowerup;
+
+ save_on_flash(GEN_ONPOWERUP_STATE);
+ }
+
+ return true;
+}
+
+static void gen_onpowerup_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ if (gen_onpowerup_setunack(model, ctx, buf) == true) {
+ gen_onpowerup_publish(model);
+ }
+}
+
+static void gen_onpowerup_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ if (gen_onpowerup_setunack(model, ctx, buf) == true) {
+ gen_onpowerup_get(model, ctx, buf);
+ gen_onpowerup_publish(model);
+ }
+}
+
+/* Vendor Model message handlers*/
+static void vnd_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(3 + 6 + 4);
+ struct vendor_state *state = model->user_data;
+
+ /* This is dummy response for demo purpose */
+ state->response = 0xA578FEB3;
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_3(0x04, CID_RUNTIME));
+ net_buf_simple_add_le16(msg, state->current);
+ net_buf_simple_add_le32(msg, state->response);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send VENDOR Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void vnd_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid;
+ int current;
+ s64_t now;
+ struct vendor_state *state = model->user_data;
+
+ current = net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ return;
+ }
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+ state->current = current;
+
+ printk("Vendor model message = %04x\n", state->current);
+
+ if (state->current == STATE_ON) {
+ /* LED2 On */
+ hal_gpio_write(led_device[1], 0);
+ } else {
+ /* LED2 Off */
+ hal_gpio_write(led_device[1], 1);
+ }
+}
+
+static void vnd_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ vnd_set_unack(model, ctx, buf);
+ vnd_get(model, ctx, buf);
+}
+
+static void vnd_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from Vendor\n");
+ printk("cmd = %04x\n", net_buf_simple_pull_le16(buf));
+ printk("response = %08lx\n", net_buf_simple_pull_le32(buf));
+}
+
+/* Light Lightness Server message handlers */
+static void light_lightness_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4);
+ struct light_lightness_state *state = model->user_data;
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS);
+ net_buf_simple_add_le16(msg, state->actual);
+
+ if (state->transition->counter) {
+ calculate_rt(state->transition);
+ net_buf_simple_add_le16(msg, state->target_actual);
+ net_buf_simple_add_u8(msg, state->transition->rt);
+ }
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send LightLightnessAct Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+void light_lightness_publish(struct bt_mesh_model *model)
+{
+ int err;
+ struct os_mbuf *msg = model->pub->msg;
+ struct light_lightness_state *state = model->user_data;
+
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS);
+ net_buf_simple_add_le16(msg, state->actual);
+
+ if (state->transition->counter) {
+ calculate_rt(state->transition);
+ net_buf_simple_add_le16(msg, state->target_actual);
+ net_buf_simple_add_u8(msg, state->transition->rt);
+ }
+
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ printk("bt_mesh_model_publish err %d\n", err);
+ }
+}
+
+static void light_lightness_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ u16_t actual;
+ s64_t now;
+ struct light_lightness_state *state = model->user_data;
+
+ actual = net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+
+ if (actual > 0 && actual < state->light_range_min) {
+ actual = state->light_range_min;
+ } else if (actual > state->light_range_max) {
+ actual = state->light_range_max;
+ }
+
+ state->target_actual = actual;
+
+ if (state->target_actual != state->actual) {
+ light_lightness_actual_tt_values(state, tt, delay);
+ } else {
+ light_lightness_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->actual = state->target_actual;
+ }
+
+ state->transition->just_started = true;
+ light_lightness_publish(model);
+ light_lightness_actual_handler(state);
+}
+
+static void light_lightness_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ u16_t actual;
+ s64_t now;
+ struct light_lightness_state *state = model->user_data;
+
+ actual = net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ light_lightness_get(model, ctx, buf);
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+
+ if (actual > 0 && actual < state->light_range_min) {
+ actual = state->light_range_min;
+ } else if (actual > state->light_range_max) {
+ actual = state->light_range_max;
+ }
+
+ state->target_actual = actual;
+
+ if (state->target_actual != state->actual) {
+ light_lightness_actual_tt_values(state, tt, delay);
+ } else {
+ light_lightness_get(model, ctx, buf);
+ light_lightness_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->actual = state->target_actual;
+ }
+
+ state->transition->just_started = true;
+ light_lightness_get(model, ctx, buf);
+ light_lightness_publish(model);
+ light_lightness_actual_handler(state);
+}
+
+static void light_lightness_linear_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4);
+ struct light_lightness_state *state = model->user_data;
+
+ bt_mesh_model_msg_init(msg,
+ BT_MESH_MODEL_LIGHT_LIGHTNESS_LINEAR_STATUS);
+ net_buf_simple_add_le16(msg, state->linear);
+
+ if (state->transition->counter) {
+ calculate_rt(state->transition);
+ net_buf_simple_add_le16(msg, state->target_linear);
+ net_buf_simple_add_u8(msg, state->transition->rt);
+ }
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send LightLightnessLin Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+void light_lightness_linear_publish(struct bt_mesh_model *model)
+{
+ int err;
+ struct os_mbuf *msg = model->pub->msg;
+ struct light_lightness_state *state = model->user_data;
+
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg,
+ BT_MESH_MODEL_LIGHT_LIGHTNESS_LINEAR_STATUS);
+ net_buf_simple_add_le16(msg, state->linear);
+
+ if (state->transition->counter) {
+ calculate_rt(state->transition);
+ net_buf_simple_add_le16(msg, state->target_linear);
+ net_buf_simple_add_u8(msg, state->transition->rt);
+ }
+
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ printk("bt_mesh_model_publish err %d\n", err);
+ }
+}
+
+static void light_lightness_linear_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ u16_t linear;
+ s64_t now;
+ struct light_lightness_state *state = model->user_data;
+
+ linear = net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+ state->target_linear = linear;
+
+ if (state->target_linear != state->linear) {
+ light_lightness_linear_tt_values(state, tt, delay);
+ } else {
+ light_lightness_linear_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->linear = state->target_linear;
+ }
+
+ state->transition->just_started = true;
+ light_lightness_linear_publish(model);
+ light_lightness_linear_handler(state);
+}
+
+static void light_lightness_linear_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ u16_t linear;
+ s64_t now;
+ struct light_lightness_state *state = model->user_data;
+
+ linear = net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ light_lightness_linear_get(model, ctx, buf);
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+ state->target_linear = linear;
+
+ if (state->target_linear != state->linear) {
+ light_lightness_linear_tt_values(state, tt, delay);
+ } else {
+ light_lightness_linear_get(model, ctx, buf);
+ light_lightness_linear_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->linear = state->target_linear;
+ }
+
+ state->transition->just_started = true;
+ light_lightness_linear_get(model, ctx, buf);
+ light_lightness_linear_publish(model);
+ light_lightness_linear_handler(state);
+}
+
+static void light_lightness_last_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4);
+ struct light_lightness_state *state = model->user_data;
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_LAST_STATUS);
+ net_buf_simple_add_le16(msg, state->last);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send LightLightnessLast Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void light_lightness_default_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4);
+ struct light_lightness_state *state = model->user_data;
+
+ bt_mesh_model_msg_init(msg,
+ BT_MESH_MODEL_LIGHT_LIGHTNESS_DEFAULT_STATUS);
+ net_buf_simple_add_le16(msg, state->def);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send LightLightnessDef Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void light_lightness_range_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4);
+ struct light_lightness_state *state = model->user_data;
+
+ state->status_code = RANGE_SUCCESSFULLY_UPDATED;
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS);
+ net_buf_simple_add_u8(msg, state->status_code);
+ net_buf_simple_add_le16(msg, state->light_range_min);
+ net_buf_simple_add_le16(msg, state->light_range_max);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send LightLightnessRange Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+/* Light Lightness Setup Server message handlers */
+
+static void light_lightness_default_publish(struct bt_mesh_model *model)
+{
+ int err;
+ struct os_mbuf *msg = model->pub->msg;
+ struct light_lightness_state *state = model->user_data;
+
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg,
+ BT_MESH_MODEL_LIGHT_LIGHTNESS_DEFAULT_STATUS);
+ net_buf_simple_add_le16(msg, state->def);
+
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ printk("bt_mesh_model_publish err %d\n", err);
+ }
+}
+
+static void light_lightness_default_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u16_t lightness;
+ struct light_lightness_state *state = model->user_data;
+
+ lightness = net_buf_simple_pull_le16(buf);
+
+ /* Here, Model specification is silent about tid implementation */
+
+ if (state->def != lightness) {
+ state->def = lightness;
+ light_ctl_srv_user_data.lightness_def = state->def;
+
+ save_on_flash(LIGHTNESS_TEMP_DEF_STATE);
+ }
+
+ light_lightness_default_publish(model);
+}
+
+static void light_lightness_default_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ light_lightness_default_set_unack(model, ctx, buf);
+ light_lightness_default_get(model, ctx, buf);
+ light_lightness_default_publish(model);
+}
+
+static void light_lightness_range_publish(struct bt_mesh_model *model)
+{
+ int err;
+ struct os_mbuf *msg = model->pub->msg;
+ struct light_lightness_state *state = model->user_data;
+
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS);
+ net_buf_simple_add_u8(msg, state->status_code);
+ net_buf_simple_add_le16(msg, state->light_range_min);
+ net_buf_simple_add_le16(msg, state->light_range_max);
+
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ printk("bt_mesh_model_publish err %d\n", err);
+ }
+}
+
+static bool light_lightness_range_setunack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u16_t min, max;
+ struct light_lightness_state *state = model->user_data;
+
+ min = net_buf_simple_pull_le16(buf);
+ max = net_buf_simple_pull_le16(buf);
+
+ /* Here, Model specification is silent about tid implementation */
+
+ if (min == 0 || max == 0) {
+ return false;
+ } else {
+ if (min <= max) {
+ state->status_code = RANGE_SUCCESSFULLY_UPDATED;
+
+ if (state->light_range_min != min ||
+ state->light_range_max != max) {
+
+ state->light_range_min = min;
+ state->light_range_max = max;
+
+ save_on_flash(LIGHTNESS_RANGE);
+ }
+ } else {
+ /* The provided value for Range Max cannot be set */
+ state->status_code = CANNOT_SET_RANGE_MAX;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void light_lightness_range_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ if (light_lightness_range_setunack(model, ctx, buf) == true) {
+ light_lightness_range_publish(model);
+ }
+}
+
+static void light_lightness_range_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ if (light_lightness_range_setunack(model, ctx, buf) == true) {
+ light_lightness_range_get(model, ctx, buf);
+ light_lightness_range_publish(model);
+ }
+}
+
+/* Light Lightness Client message handlers */
+static void light_lightness_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Actual)\n");
+ printk("Present Lightness = %04x\n", net_buf_simple_pull_le16(buf));
+
+ if (buf->om_len == 3) {
+ printk("Target Lightness = %04x\n",
+ net_buf_simple_pull_le16(buf));
+ printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf));
+ }
+}
+
+static void light_lightness_linear_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Linear)\n");
+ printk("Present Lightness = %04x\n", net_buf_simple_pull_le16(buf));
+
+ if (buf->om_len == 3) {
+ printk("Target Lightness = %04x\n",
+ net_buf_simple_pull_le16(buf));
+ printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf));
+ }
+}
+
+static void light_lightness_last_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Last)\n");
+ printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf));
+}
+
+static void light_lightness_default_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Default)\n");
+ printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf));
+}
+
+static void light_lightness_range_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Lightness Range)\n");
+ printk("Status Code = %02x\n", net_buf_simple_pull_u8(buf));
+ printk("Range Min = %04x\n", net_buf_simple_pull_le16(buf));
+ printk("Range Max = %04x\n", net_buf_simple_pull_le16(buf));
+}
+
+/* Light CTL Server message handlers */
+static void light_ctl_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + 4);
+ struct light_ctl_state *state = model->user_data;
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_STATUS);
+ net_buf_simple_add_le16(msg, state->lightness);
+ net_buf_simple_add_le16(msg, state->temp);
+
+ if (state->transition->counter) {
+ calculate_rt(state->transition);
+ net_buf_simple_add_le16(msg, state->target_lightness);
+ net_buf_simple_add_le16(msg, state->target_temp);
+ net_buf_simple_add_u8(msg, state->transition->rt);
+ }
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send LightCTL Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+void light_ctl_publish(struct bt_mesh_model *model)
+{
+ int err;
+ struct os_mbuf *msg = model->pub->msg;
+ struct light_ctl_state *state = model->user_data;
+
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_STATUS);
+
+ /* Here, as per Model specification, status should be
+ * made up of lightness & temperature values only
+ */
+ net_buf_simple_add_le16(msg, state->lightness);
+ net_buf_simple_add_le16(msg, state->temp);
+
+ if (state->transition->counter) {
+ calculate_rt(state->transition);
+ net_buf_simple_add_le16(msg, state->target_lightness);
+ net_buf_simple_add_le16(msg, state->target_temp);
+ net_buf_simple_add_u8(msg, state->transition->rt);
+ }
+
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ printk("bt_mesh_model_publish err %d\n", err);
+ }
+}
+
+static void light_ctl_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ s16_t delta_uv;
+ u16_t lightness, temp;
+ s64_t now;
+ struct light_ctl_state *state = model->user_data;
+
+ lightness = net_buf_simple_pull_le16(buf);
+ temp = net_buf_simple_pull_le16(buf);
+ delta_uv = (s16_t) net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (temp < TEMP_MIN || temp > TEMP_MAX) {
+ return;
+ }
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+ state->target_lightness = lightness;
+
+ if (temp < state->temp_range_min) {
+ temp = state->temp_range_min;
+ } else if (temp > state->temp_range_max) {
+ temp = state->temp_range_max;
+ }
+
+ state->target_temp = temp;
+ state->target_delta_uv = delta_uv;
+
+ if (state->target_lightness != state->lightness ||
+ state->target_temp != state->temp ||
+ state->target_delta_uv != state->delta_uv) {
+ light_ctl_tt_values(state, tt, delay);
+ } else {
+ light_ctl_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->lightness = state->target_lightness;
+ state->temp = state->target_temp;
+ state->delta_uv = state->target_delta_uv;
+ }
+
+ state->transition->just_started = true;
+ light_ctl_publish(model);
+ light_ctl_handler(state);
+}
+
+static void light_ctl_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ s16_t delta_uv;
+ u16_t lightness, temp;
+ s64_t now;
+ struct light_ctl_state *state = model->user_data;
+
+ lightness = net_buf_simple_pull_le16(buf);
+ temp = net_buf_simple_pull_le16(buf);
+ delta_uv = (s16_t) net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (temp < TEMP_MIN || temp > TEMP_MAX) {
+ return;
+ }
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ light_ctl_get(model, ctx, buf);
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+ state->target_lightness = lightness;
+
+ if (temp < state->temp_range_min) {
+ temp = state->temp_range_min;
+ } else if (temp > state->temp_range_max) {
+ temp = state->temp_range_max;
+ }
+
+ state->target_temp = temp;
+ state->target_delta_uv = delta_uv;
+
+ if (state->target_lightness != state->lightness ||
+ state->target_temp != state->temp ||
+ state->target_delta_uv != state->delta_uv) {
+ light_ctl_tt_values(state, tt, delay);
+ } else {
+ light_ctl_get(model, ctx, buf);
+ light_ctl_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->lightness = state->target_lightness;
+ state->temp = state->target_temp;
+ state->delta_uv = state->target_delta_uv;
+ }
+
+ state->transition->just_started = true;
+ light_ctl_get(model, ctx, buf);
+ light_ctl_publish(model);
+ light_ctl_handler(state);
+}
+
+static void light_ctl_temp_range_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4);
+ struct light_ctl_state *state = model->user_data;
+
+ state->status_code = RANGE_SUCCESSFULLY_UPDATED;
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS);
+ net_buf_simple_add_u8(msg, state->status_code);
+ net_buf_simple_add_le16(msg, state->temp_range_min);
+ net_buf_simple_add_le16(msg, state->temp_range_max);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send LightCTL Temp Range Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void light_ctl_default_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 6 + 4);
+ struct light_ctl_state *state = model->user_data;
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS);
+ net_buf_simple_add_le16(msg, state->lightness_def);
+ net_buf_simple_add_le16(msg, state->temp_def);
+ net_buf_simple_add_le16(msg, state->delta_uv_def);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send LightCTL Default Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+/* Light CTL Setup Server message handlers */
+
+static void light_ctl_default_publish(struct bt_mesh_model *model)
+{
+ int err;
+ struct os_mbuf *msg = model->pub->msg;
+ struct light_ctl_state *state = model->user_data;
+
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS);
+ net_buf_simple_add_le16(msg, state->lightness_def);
+ net_buf_simple_add_le16(msg, state->temp_def);
+ net_buf_simple_add_le16(msg, state->delta_uv_def);
+
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ printk("bt_mesh_model_publish err %d\n", err);
+ }
+}
+
+static bool light_ctl_default_setunack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u16_t lightness, temp;
+ s16_t delta_uv;
+ struct light_ctl_state *state = model->user_data;
+
+ lightness = net_buf_simple_pull_le16(buf);
+ temp = net_buf_simple_pull_le16(buf);
+ delta_uv = (s16_t) net_buf_simple_pull_le16(buf);
+
+ /* Here, Model specification is silent about tid implementation */
+
+ if (temp < TEMP_MIN || temp > TEMP_MAX) {
+ return false;
+ }
+
+ if (temp < state->temp_range_min) {
+ temp = state->temp_range_min;
+ } else if (temp > state->temp_range_max) {
+ temp = state->temp_range_max;
+ }
+
+ if (state->lightness_def != lightness || state->temp_def != temp ||
+ state->delta_uv_def != delta_uv) {
+ state->lightness_def = lightness;
+ state->temp_def = temp;
+ state->delta_uv_def = delta_uv;
+
+ save_on_flash(LIGHTNESS_TEMP_DEF_STATE);
+ }
+
+ return true;
+}
+
+static void light_ctl_default_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ if (light_ctl_default_setunack(model, ctx, buf) == true) {
+ light_ctl_default_publish(model);
+ }
+}
+
+static void light_ctl_default_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ if (light_ctl_default_setunack(model, ctx, buf) == true) {
+ light_ctl_default_get(model, ctx, buf);
+ light_ctl_default_publish(model);
+ }
+}
+
+static void light_ctl_temp_range_publish(struct bt_mesh_model *model)
+{
+ int err;
+ struct os_mbuf *msg = model->pub->msg;
+ struct light_ctl_state *state = model->user_data;
+
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS);
+ net_buf_simple_add_u8(msg, state->status_code);
+ net_buf_simple_add_le16(msg, state->temp_range_min);
+ net_buf_simple_add_le16(msg, state->temp_range_max);
+
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ printk("bt_mesh_model_publish err %d\n", err);
+ }
+}
+
+static bool light_ctl_temp_range_setunack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u16_t min, max;
+ struct light_ctl_state *state = model->user_data;
+
+ min = net_buf_simple_pull_le16(buf);
+ max = net_buf_simple_pull_le16(buf);
+
+ /* Here, Model specification is silent about tid implementation */
+
+ /* This is as per 6.1.3.1 in Mesh Model Specification */
+ if (min < TEMP_MIN || min > TEMP_MAX ||
+ max < TEMP_MIN || max > TEMP_MAX) {
+ return false;
+ }
+
+ if (min <= max) {
+ state->status_code = RANGE_SUCCESSFULLY_UPDATED;
+
+ if (state->temp_range_min != min ||
+ state->temp_range_max != max) {
+
+ state->temp_range_min = min;
+ state->temp_range_max = max;
+
+ save_on_flash(TEMPERATURE_RANGE);
+ }
+ } else {
+ /* The provided value for Range Max cannot be set */
+ state->status_code = CANNOT_SET_RANGE_MAX;
+ return false;
+ }
+
+ return true;
+}
+
+static void light_ctl_temp_range_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ if (light_ctl_temp_range_setunack(model, ctx, buf) == true) {
+ light_ctl_temp_range_publish(model);
+ }
+}
+
+static void light_ctl_temp_range_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ if (light_ctl_temp_range_setunack(model, ctx, buf) == true) {
+ light_ctl_temp_range_get(model, ctx, buf);
+ light_ctl_temp_range_publish(model);
+ }
+}
+
+/* Light CTL Client message handlers */
+static void light_ctl_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from LIGHT_CTL_SRV\n");
+ printk("Present CTL Lightness = %04x\n", net_buf_simple_pull_le16(buf));
+ printk("Present CTL Temperature = %04x\n",
+ net_buf_simple_pull_le16(buf));
+
+ if (buf->om_len == 5) {
+ printk("Target CTL Lightness = %04x\n",
+ net_buf_simple_pull_le16(buf));
+ printk("Target CTL Temperature = %04x\n",
+ net_buf_simple_pull_le16(buf));
+ printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf));
+ }
+}
+
+static void light_ctl_temp_range_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from LIGHT_CTL_SRV (Temperature Range)\n");
+ printk("Status Code = %02x\n", net_buf_simple_pull_u8(buf));
+ printk("Range Min = %04x\n", net_buf_simple_pull_le16(buf));
+ printk("Range Max = %04x\n", net_buf_simple_pull_le16(buf));
+}
+
+static void light_ctl_temp_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from LIGHT_CTL_TEMP_SRV\n");
+ printk("Present CTL Temperature = %04x\n",
+ net_buf_simple_pull_le16(buf));
+ printk("Present CTL Delta UV = %04x\n",
+ net_buf_simple_pull_le16(buf));
+
+ if (buf->om_len == 5) {
+ printk("Target CTL Temperature = %04x\n",
+ net_buf_simple_pull_le16(buf));
+ printk("Target CTL Delta UV = %04x\n",
+ net_buf_simple_pull_le16(buf));
+ printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf));
+ }
+}
+
+static void light_ctl_default_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ printk("Acknownledgement from LIGHT_CTL_SRV (Default)\n");
+ printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf));
+ printk("Temperature = %04x\n", net_buf_simple_pull_le16(buf));
+ printk("Delta UV = %04x\n", net_buf_simple_pull_le16(buf));
+}
+
+/* Light CTL Temp. Server message handlers */
+static void light_ctl_temp_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + 4);
+ struct light_ctl_state *state = model->user_data;
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS);
+ net_buf_simple_add_le16(msg, state->temp);
+ net_buf_simple_add_le16(msg, state->delta_uv);
+
+ if (state->transition->counter) {
+ calculate_rt(state->transition);
+ net_buf_simple_add_le16(msg, state->target_temp);
+ net_buf_simple_add_le16(msg, state->target_delta_uv);
+ net_buf_simple_add_u8(msg, state->transition->rt);
+ }
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ printk("Unable to send LightCTL Temp. Status response\n");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+void light_ctl_temp_publish(struct bt_mesh_model *model)
+{
+ int err;
+ struct os_mbuf *msg = model->pub->msg;
+ struct light_ctl_state *state = model->user_data;
+
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS);
+ net_buf_simple_add_le16(msg, state->temp);
+ net_buf_simple_add_le16(msg, state->delta_uv);
+
+ if (state->transition->counter) {
+ calculate_rt(state->transition);
+ net_buf_simple_add_le16(msg, state->target_temp);
+ net_buf_simple_add_le16(msg, state->target_delta_uv);
+ net_buf_simple_add_u8(msg, state->transition->rt);
+ }
+
+ err = bt_mesh_model_publish(model);
+ if (err) {
+ printk("bt_mesh_model_publish err %d\n", err);
+ }
+}
+
+static void light_ctl_temp_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ s16_t delta_uv;
+ u16_t temp;
+ s64_t now;
+ struct light_ctl_state *state = model->user_data;
+
+ temp = net_buf_simple_pull_le16(buf);
+ delta_uv = (s16_t) net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (temp < TEMP_MIN || temp > TEMP_MAX) {
+ return;
+ }
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+
+ if (temp < state->temp_range_min) {
+ temp = state->temp_range_min;
+ } else if (temp > state->temp_range_max) {
+ temp = state->temp_range_max;
+ }
+
+ state->target_temp = temp;
+ state->target_delta_uv = delta_uv;
+
+ if (state->target_temp != state->temp ||
+ state->target_delta_uv != state->delta_uv) {
+ light_ctl_temp_tt_values(state, tt, delay);
+ } else {
+ light_ctl_temp_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->temp = state->target_temp;
+ state->delta_uv = state->target_delta_uv;
+ }
+
+ state->transition->just_started = true;
+ light_ctl_temp_publish(model);
+ light_ctl_temp_handler(state);
+}
+
+static void light_ctl_temp_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t tid, tt, delay;
+ s16_t delta_uv;
+ u16_t temp;
+ s64_t now;
+ struct light_ctl_state *state = model->user_data;
+
+ temp = net_buf_simple_pull_le16(buf);
+ delta_uv = (s16_t) net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (temp < TEMP_MIN || temp > TEMP_MAX) {
+ return;
+ }
+
+ now = k_uptime_get();
+ if (state->last_tid == tid &&
+ state->last_src_addr == ctx->addr &&
+ state->last_dst_addr == ctx->recv_dst &&
+ (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+ light_ctl_temp_get(model, ctx, buf);
+ return;
+ }
+
+ switch (buf->om_len) {
+ case 0x00: /* No optional fields are available */
+ tt = default_tt;
+ delay = 0;
+ break;
+ case 0x02: /* Optional fields are available */
+ tt = net_buf_simple_pull_u8(buf);
+ if ((tt & 0x3F) == 0x3F) {
+ return;
+ }
+
+ delay = net_buf_simple_pull_u8(buf);
+ break;
+ default:
+ return;
+ }
+
+ *ptr_counter = 0;
+ os_callout_stop(ptr_timer);
+
+ state->last_tid = tid;
+ state->last_src_addr = ctx->addr;
+ state->last_dst_addr = ctx->recv_dst;
+ state->last_msg_timestamp = now;
+
+ if (temp < state->temp_range_min) {
+ temp = state->temp_range_min;
+ } else if (temp > state->temp_range_max) {
+ temp = state->temp_range_max;
+ }
+
+ state->target_temp = temp;
+ state->target_delta_uv = delta_uv;
+
+ if (state->target_temp != state->temp ||
+ state->target_delta_uv != state->delta_uv) {
+ light_ctl_temp_tt_values(state, tt, delay);
+ } else {
+ light_ctl_temp_get(model, ctx, buf);
+ light_ctl_temp_publish(model);
+ return;
+ }
+
+ /* For Instantaneous Transition */
+ if (state->transition->counter == 0) {
+ state->temp = state->target_temp;
+ state->delta_uv = state->target_delta_uv;
+ }
+
+ state->transition->just_started = true;
+ light_ctl_temp_get(model, ctx, buf);
+ light_ctl_temp_publish(model);
+ light_ctl_temp_handler(state);
+}
+
+/* message handlers (End) */
+
+/* Mapping of message handlers for Generic OnOff Server (0x1000) */
+static const struct bt_mesh_model_op gen_onoff_srv_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x01), 0, gen_onoff_get },
+ { BT_MESH_MODEL_OP_2(0x82, 0x02), 2, gen_onoff_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x03), 2, gen_onoff_set_unack },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic OnOff Client (0x1001) */
+static const struct bt_mesh_model_op gen_onoff_cli_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x04), 1, gen_onoff_status },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Levl Server (0x1002) */
+static const struct bt_mesh_model_op gen_level_srv_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x05), 0, gen_level_get },
+ { BT_MESH_MODEL_OP_2(0x82, 0x06), 3, gen_level_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x07), 3, gen_level_set_unack },
+ { BT_MESH_MODEL_OP_2(0x82, 0x09), 5, gen_delta_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x0A), 5, gen_delta_set_unack },
+ { BT_MESH_MODEL_OP_2(0x82, 0x0B), 3, gen_move_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x0C), 3, gen_move_set_unack },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Level Client (0x1003) */
+static const struct bt_mesh_model_op gen_level_cli_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x08), 2, gen_level_status },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Default TT Server (0x1004) */
+static const struct bt_mesh_model_op gen_def_trans_time_srv_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x0D), 0, gen_def_trans_time_get },
+ { BT_MESH_MODEL_OP_2(0x82, 0x0E), 1, gen_def_trans_time_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x0F), 1, gen_def_trans_time_set_unack },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Default TT Client (0x1005) */
+static const struct bt_mesh_model_op gen_def_trans_time_cli_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x10), 1, gen_def_trans_time_status },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Power OnOff Server (0x1006) */
+static const struct bt_mesh_model_op gen_power_onoff_srv_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x11), 0, gen_onpowerup_get },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Power OnOff Setup Server (0x1007) */
+static const struct bt_mesh_model_op gen_power_onoff_setup_srv_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x13), 1, gen_onpowerup_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x14), 1, gen_onpowerup_set_unack },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Power OnOff Client (0x1008) */
+static const struct bt_mesh_model_op gen_power_onoff_cli_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x12), 1, gen_onpowerup_status },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light Lightness Server (0x1300) */
+static const struct bt_mesh_model_op light_lightness_srv_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x4B), 0, light_lightness_get },
+ { BT_MESH_MODEL_OP_2(0x82, 0x4C), 3, light_lightness_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x4D), 3, light_lightness_set_unack },
+ { BT_MESH_MODEL_OP_2(0x82, 0x4F), 0, light_lightness_linear_get },
+ { BT_MESH_MODEL_OP_2(0x82, 0x50), 3, light_lightness_linear_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x51), 3,
+ light_lightness_linear_set_unack },
+ { BT_MESH_MODEL_OP_2(0x82, 0x53), 0, light_lightness_last_get },
+ { BT_MESH_MODEL_OP_2(0x82, 0x55), 0, light_lightness_default_get },
+ { BT_MESH_MODEL_OP_2(0x82, 0x57), 0, light_lightness_range_get },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light Lightness Setup Server (0x1301) */
+static const struct bt_mesh_model_op light_lightness_setup_srv_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x59), 2, light_lightness_default_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x5A), 2,
+ light_lightness_default_set_unack },
+ { BT_MESH_MODEL_OP_2(0x82, 0x5B), 4, light_lightness_range_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x5C), 4, light_lightness_range_set_unack },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light Lightness Client (0x1302) */
+static const struct bt_mesh_model_op light_lightness_cli_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x4E), 2, light_lightness_status },
+ { BT_MESH_MODEL_OP_2(0x82, 0x52), 2, light_lightness_linear_status },
+ { BT_MESH_MODEL_OP_2(0x82, 0x54), 2, light_lightness_last_status },
+ { BT_MESH_MODEL_OP_2(0x82, 0x56), 2, light_lightness_default_status },
+ { BT_MESH_MODEL_OP_2(0x82, 0x58), 5, light_lightness_range_status },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light CTL Server (0x1303) */
+static const struct bt_mesh_model_op light_ctl_srv_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x5D), 0, light_ctl_get },
+ { BT_MESH_MODEL_OP_2(0x82, 0x5E), 7, light_ctl_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x5F), 7, light_ctl_set_unack },
+ { BT_MESH_MODEL_OP_2(0x82, 0x62), 0, light_ctl_temp_range_get },
+ { BT_MESH_MODEL_OP_2(0x82, 0x67), 0, light_ctl_default_get },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light CTL Setup Server (0x1304) */
+static const struct bt_mesh_model_op light_ctl_setup_srv_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x69), 6, light_ctl_default_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x6A), 6, light_ctl_default_set_unack },
+ { BT_MESH_MODEL_OP_2(0x82, 0x6B), 4, light_ctl_temp_range_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x6C), 4, light_ctl_temp_range_set_unack },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light CTL Client (0x1305) */
+static const struct bt_mesh_model_op light_ctl_cli_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x60), 4, light_ctl_status },
+ { BT_MESH_MODEL_OP_2(0x82, 0x63), 5, light_ctl_temp_range_status },
+ { BT_MESH_MODEL_OP_2(0x82, 0x66), 4, light_ctl_temp_status },
+ { BT_MESH_MODEL_OP_2(0x82, 0x68), 6, light_ctl_default_status },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light CTL Temp. Server (0x1306) */
+static const struct bt_mesh_model_op light_ctl_temp_srv_op[] = {
+ { BT_MESH_MODEL_OP_2(0x82, 0x61), 0, light_ctl_temp_get },
+ { BT_MESH_MODEL_OP_2(0x82, 0x64), 5, light_ctl_temp_set },
+ { BT_MESH_MODEL_OP_2(0x82, 0x65), 5, light_ctl_temp_set_unack },
+ BT_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Vendor (0x4321) */
+static const struct bt_mesh_model_op vnd_ops[] = {
+ { BT_MESH_MODEL_OP_3(0x01, CID_RUNTIME), 0, vnd_get },
+ { BT_MESH_MODEL_OP_3(0x02, CID_RUNTIME), 3, vnd_set },
+ { BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME), 3, vnd_set_unack },
+ { BT_MESH_MODEL_OP_3(0x04, CID_RUNTIME), 6, vnd_status },
+ BT_MESH_MODEL_OP_END,
+};
+
+struct bt_mesh_model root_models[] = {
+ BT_MESH_MODEL_CFG_SRV(&cfg_srv),
+ BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
+
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV,
+ gen_onoff_srv_op, &gen_onoff_srv_pub_root,
+ &gen_onoff_srv_root_user_data),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI,
+ gen_onoff_cli_op, &gen_onoff_cli_pub_root,
+ NULL),
+
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV,
+ gen_level_srv_op, &gen_level_srv_pub_root,
+ &gen_level_srv_root_user_data),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_CLI,
+ gen_level_cli_op, &gen_level_cli_pub_root,
+ NULL),
+
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV,
+ gen_def_trans_time_srv_op,
+ &gen_def_trans_time_srv_pub,
+ &gen_def_trans_time_srv_user_data),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI,
+ gen_def_trans_time_cli_op,
+ &gen_def_trans_time_cli_pub,
+ NULL),
+
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV,
+ gen_power_onoff_srv_op, &gen_power_onoff_srv_pub,
+ &gen_power_onoff_srv_user_data),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV,
+ gen_power_onoff_setup_srv_op,
+ &gen_power_onoff_srv_pub,
+ &gen_power_onoff_srv_user_data),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI,
+ gen_power_onoff_cli_op, &gen_power_onoff_cli_pub,
+ NULL),
+
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV,
+ light_lightness_srv_op, &light_lightness_srv_pub,
+ &light_lightness_srv_user_data),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV,
+ light_lightness_setup_srv_op,
+ &light_lightness_srv_pub,
+ &light_lightness_srv_user_data),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI,
+ light_lightness_cli_op, &light_lightness_cli_pub,
+ NULL),
+
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_SRV,
+ light_ctl_srv_op, &light_ctl_srv_pub,
+ &light_ctl_srv_user_data),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV,
+ light_ctl_setup_srv_op, &light_ctl_srv_pub,
+ &light_ctl_srv_user_data),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_CLI,
+ light_ctl_cli_op, &light_ctl_cli_pub,
+ NULL),
+};
+
+struct bt_mesh_model vnd_models[] = {
+ BT_MESH_MODEL_VND(CID_RUNTIME, 0x4321, vnd_ops,
+ &vnd_pub, &vnd_user_data),
+};
+
+struct bt_mesh_model s0_models[] = {
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV,
+ gen_level_srv_op, &gen_level_srv_pub_s0,
+ &gen_level_srv_s0_user_data),
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_CLI,
+ gen_level_cli_op, &gen_level_cli_pub_s0,
+ NULL),
+
+ BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV,
+ light_ctl_temp_srv_op, &light_ctl_srv_pub,
+ &light_ctl_srv_user_data),
+};
+
+static struct bt_mesh_elem elements[] = {
+ BT_MESH_ELEM(0, root_models, vnd_models),
+ BT_MESH_ELEM(0, s0_models, BT_MESH_MODEL_NONE),
+};
+
+const struct bt_mesh_comp comp = {
+ .cid = CID_RUNTIME,
+ .elem = elements,
+ .elem_count = ARRAY_SIZE(elements),
+};
+
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.h
new file mode 100644
index 00000000..38507195
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.h
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _DEVICE_COMPOSITION_H
+#define _DEVICE_COMPOSITION_H
+
+#define CID_RUNTIME 0x05C3
+
+#define STATE_OFF 0x00
+#define STATE_ON 0x01
+#define STATE_DEFAULT 0x01
+#define STATE_RESTORE 0x02
+
+/* Following 4 values are as per Mesh Model specification */
+#define LIGHTNESS_MIN 0x0001
+#define LIGHTNESS_MAX 0xFFFF
+#define TEMP_MIN 0x0320
+#define TEMP_MAX 0x4E20
+
+/* Refer 7.2 of Mesh Model Specification */
+#define RANGE_SUCCESSFULLY_UPDATED 0x00
+#define CANNOT_SET_RANGE_MIN 0x01
+#define CANNOT_SET_RANGE_MAX 0x02
+
+struct generic_onoff_state {
+ u8_t onoff;
+ u8_t target_onoff;
+
+ u8_t last_tid;
+ u16_t last_src_addr;
+ u16_t last_dst_addr;
+ s64_t last_msg_timestamp;
+
+ s32_t tt_delta;
+
+ struct transition *transition;
+};
+
+struct generic_level_state {
+ s16_t level;
+ s16_t target_level;
+
+ s16_t last_level;
+ s32_t last_delta;
+
+ u8_t last_tid;
+ u16_t last_src_addr;
+ u16_t last_dst_addr;
+ s64_t last_msg_timestamp;
+
+ s32_t tt_delta;
+
+ struct transition *transition;
+};
+
+struct generic_onpowerup_state {
+ u8_t onpowerup;
+};
+
+struct gen_def_trans_time_state {
+ u8_t tt;
+};
+
+struct vendor_state {
+ int current;
+ u32_t response;
+ u8_t last_tid;
+ u16_t last_src_addr;
+ u16_t last_dst_addr;
+ s64_t last_msg_timestamp;
+};
+
+struct light_lightness_state {
+ u16_t linear;
+ u16_t target_linear;
+
+ u16_t actual;
+ u16_t target_actual;
+
+ u16_t last;
+ u16_t def;
+
+ u8_t status_code;
+ u16_t light_range_min;
+ u16_t light_range_max;
+ u32_t lightness_range;
+
+ u8_t last_tid;
+ u16_t last_src_addr;
+ u16_t last_dst_addr;
+ s64_t last_msg_timestamp;
+
+ s32_t tt_delta_actual;
+ s32_t tt_delta_linear;
+
+ struct transition *transition;
+};
+
+struct light_ctl_state {
+ u16_t lightness;
+ u16_t target_lightness;
+
+ u16_t temp;
+ u16_t target_temp;
+
+ s16_t delta_uv;
+ s16_t target_delta_uv;
+
+ u8_t status_code;
+ u16_t temp_range_min;
+ u16_t temp_range_max;
+ u32_t temperature_range;
+
+ u16_t lightness_def;
+ u16_t temp_def;
+ u32_t lightness_temp_def;
+ s16_t delta_uv_def;
+
+ u32_t lightness_temp_last;
+
+ u8_t last_tid;
+ u16_t last_src_addr;
+ u16_t last_dst_addr;
+ s64_t last_msg_timestamp;
+
+ s32_t tt_delta_lightness;
+ s32_t tt_delta_temp;
+ s32_t tt_delta_duv;
+
+ struct transition *transition;
+};
+
+extern struct generic_onoff_state gen_onoff_srv_root_user_data;
+extern struct generic_level_state gen_level_srv_root_user_data;
+extern struct gen_def_trans_time_state gen_def_trans_time_srv_user_data;
+extern struct generic_onpowerup_state gen_power_onoff_srv_user_data;
+extern struct light_lightness_state light_lightness_srv_user_data;
+extern struct light_ctl_state light_ctl_srv_user_data;
+extern struct generic_level_state gen_level_srv_s0_user_data;
+
+extern struct bt_mesh_model root_models[];
+extern struct bt_mesh_model vnd_models[];
+extern struct bt_mesh_model s0_models[];
+
+extern const struct bt_mesh_comp comp;
+
+void gen_onoff_publish(struct bt_mesh_model *model);
+void gen_level_publish(struct bt_mesh_model *model);
+void light_lightness_publish(struct bt_mesh_model *model);
+void light_lightness_linear_publish(struct bt_mesh_model *model);
+void light_ctl_publish(struct bt_mesh_model *model);
+void light_ctl_temp_publish(struct bt_mesh_model *model);
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/main.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/main.c
new file mode 100644
index 00000000..7c8d65e6
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/main.c
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+#include "console/console.h"
+#include "hal/hal_gpio.h"
+#include "mesh/mesh.h"
+
+#include "app_gpio.h"
+#include "storage.h"
+
+#include "ble_mesh.h"
+#include "device_composition.h"
+#include "no_transition_work_handler.h"
+#include "publisher.h"
+#include "state_binding.h"
+#include "transition.h"
+
+static bool reset;
+
+static void light_default_var_init(void)
+{
+ gen_def_trans_time_srv_user_data.tt = 0x00;
+
+ gen_power_onoff_srv_user_data.onpowerup = STATE_DEFAULT;
+
+ light_lightness_srv_user_data.light_range_min = LIGHTNESS_MIN;
+ light_lightness_srv_user_data.light_range_max = LIGHTNESS_MAX;
+ light_lightness_srv_user_data.last = LIGHTNESS_MAX;
+ light_lightness_srv_user_data.def = LIGHTNESS_MAX;
+
+ /* Following 2 values are as per specification */
+ light_ctl_srv_user_data.temp_range_min = TEMP_MIN;
+ light_ctl_srv_user_data.temp_range_max = TEMP_MAX;
+
+ light_ctl_srv_user_data.temp_def = TEMP_MIN;
+
+ light_ctl_srv_user_data.lightness_temp_last =
+ (u32_t) ((LIGHTNESS_MAX << 16) | TEMP_MIN);
+}
+
+static void light_default_status_init(void)
+{
+ u16_t lightness;
+
+ lightness = (u16_t) (light_ctl_srv_user_data.lightness_temp_last >> 16);
+
+ if (lightness) {
+ gen_onoff_srv_root_user_data.onoff = STATE_ON;
+ } else {
+ gen_onoff_srv_root_user_data.onoff = STATE_OFF;
+ }
+
+ /* Retrieve Default Lightness & Temperature Values */
+
+ if (light_ctl_srv_user_data.lightness_temp_def) {
+ light_ctl_srv_user_data.lightness_def = (u16_t)
+ (light_ctl_srv_user_data.lightness_temp_def >> 16);
+
+ light_ctl_srv_user_data.temp_def = (u16_t)
+ (light_ctl_srv_user_data.lightness_temp_def);
+ }
+
+ light_lightness_srv_user_data.def =
+ light_ctl_srv_user_data.lightness_def;
+
+ light_ctl_srv_user_data.temp = light_ctl_srv_user_data.temp_def;
+
+ /* Retrieve Range of Lightness & Temperature */
+
+ if (light_lightness_srv_user_data.lightness_range) {
+ light_lightness_srv_user_data.light_range_max = (u16_t)
+ (light_lightness_srv_user_data.lightness_range >> 16);
+
+ light_lightness_srv_user_data.light_range_min = (u16_t)
+ (light_lightness_srv_user_data.lightness_range);
+ }
+
+ if (light_ctl_srv_user_data.temperature_range) {
+ light_ctl_srv_user_data.temp_range_max = (u16_t)
+ (light_ctl_srv_user_data.temperature_range >> 16);
+
+ light_ctl_srv_user_data.temp_range_min = (u16_t)
+ (light_ctl_srv_user_data.temperature_range);
+ }
+
+ switch (gen_power_onoff_srv_user_data.onpowerup) {
+ case STATE_OFF:
+ gen_onoff_srv_root_user_data.onoff = STATE_OFF;
+ state_binding(ONOFF, ONOFF_TEMP);
+ break;
+ case STATE_DEFAULT:
+ gen_onoff_srv_root_user_data.onoff = STATE_ON;
+ state_binding(ONOFF, ONOFF_TEMP);
+ break;
+ case STATE_RESTORE:
+ light_lightness_srv_user_data.last = (u16_t)
+ (light_ctl_srv_user_data.lightness_temp_last >> 16);
+
+ light_ctl_srv_user_data.temp =
+ (u16_t) (light_ctl_srv_user_data.lightness_temp_last);
+
+ state_binding(ONPOWERUP, ONOFF_TEMP);
+ break;
+ }
+
+ default_tt = gen_def_trans_time_srv_user_data.tt;
+}
+
+void update_light_state(void)
+{
+ u8_t power, color;
+
+ power = 100 * ((float) lightness / 65535);
+ color = 100 * ((float) (temperature + 32768) / 65535);
+
+ printk("power-> %d, color-> %d\n", power, color);
+
+ if (lightness) {
+ /* LED1 On */
+ hal_gpio_write(led_device[0], 0);
+ } else {
+ /* LED1 Off */
+ hal_gpio_write(led_device[0], 1);
+ }
+
+ if (power < 50) {
+ /* LED3 On */
+ hal_gpio_write(led_device[2], 0);
+ } else {
+ /* LED3 Off */
+ hal_gpio_write(led_device[2], 1);
+ }
+
+ if (color < 50) {
+ /* LED4 On */
+ hal_gpio_write(led_device[3], 0);
+ } else {
+ /* LED4 Off */
+ hal_gpio_write(led_device[3], 1);
+ }
+
+ if (*ptr_counter == 0 || reset == false) {
+ reset = true;
+ os_callout_reset(&no_transition_work, 0);
+ }
+}
+
+static void short_time_multireset_bt_mesh_unprovisioning(void)
+{
+ if (reset_counter >= 4) {
+ reset_counter = 0;
+ printk("BT Mesh reset\n");
+ bt_mesh_reset();
+ } else {
+ printk("Reset Counter -> %d\n", reset_counter);
+ reset_counter++;
+ }
+
+ save_on_flash(RESET_COUNTER);
+}
+
+static void reset_counter_timer_handler(struct os_event *dummy)
+{
+ reset_counter = 0;
+ save_on_flash(RESET_COUNTER);
+ printk("Reset Counter set to Zero\n");
+}
+
+struct os_callout reset_counter_timer;
+
+static void init_timers(void)
+{
+
+ os_callout_init(&reset_counter_timer, os_eventq_dflt_get(),
+ reset_counter_timer_handler, NULL);
+ os_callout_reset(&reset_counter_timer,
+ os_time_ms_to_ticks32(K_MSEC(7000)));
+
+ no_transition_work_init();
+}
+
+void bt_initialized(void)
+{
+ light_default_status_init();
+
+ update_light_state();
+
+ randomize_publishers_TID();
+
+ short_time_multireset_bt_mesh_unprovisioning();
+}
+
+int main(void)
+{
+#ifdef ARCH_sim
+ mcu_sim_parse_args(argc, argv);
+#endif
+
+ /* Initialize OS */
+ sysinit();
+
+ light_default_var_init();
+
+ app_gpio_init();
+
+ init_timers();
+
+ transition_timers_init();
+
+ init_pub();
+
+ ps_settings_init();
+
+ printk("Initializing...\n");
+
+ /* Initialize the NimBLE host configuration. */
+ ble_hs_cfg.reset_cb = blemesh_on_reset;
+ ble_hs_cfg.sync_cb = blemesh_on_sync;
+ ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
+
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.c
new file mode 100644
index 00000000..58630bdc
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.c
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "ble_mesh.h"
+#include "device_composition.h"
+
+#include "storage.h"
+
+static void unsolicitedly_publish_states_work_handler(struct os_event *work)
+{
+ gen_onoff_publish(&root_models[2]);
+ gen_level_publish(&root_models[4]);
+ light_lightness_publish(&root_models[11]);
+ light_lightness_linear_publish(&root_models[11]);
+ light_ctl_publish(&root_models[14]);
+
+ gen_level_publish(&s0_models[0]);
+ light_ctl_temp_publish(&s0_models[2]);
+}
+
+struct os_callout unsolicitedly_publish_states_work;
+
+static void unsolicitedly_publish_states_timer_handler(struct os_event *dummy)
+{
+ os_callout_reset(&unsolicitedly_publish_states_work, 0);
+}
+
+struct os_callout unsolicitedly_publish_states_timer;
+
+static void save_lightness_temp_last_values_timer_handler(struct os_event *dummy)
+{
+ save_on_flash(LIGHTNESS_TEMP_LAST_STATE);
+}
+
+struct os_callout save_lightness_temp_last_values_timer;
+
+static void no_transition_work_handler(struct os_event *work)
+{
+ os_callout_reset(&unsolicitedly_publish_states_timer,
+ os_time_ms_to_ticks32(K_MSEC(5000)));
+
+ /* If Lightness & Temperature values remains stable for
+ * 10 Seconds then & then only get stored on SoC flash.
+ */
+ if (gen_power_onoff_srv_user_data.onpowerup == STATE_RESTORE) {
+ os_callout_reset(&save_lightness_temp_last_values_timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(10000)));
+ }
+}
+
+struct os_callout no_transition_work;
+
+void no_transition_work_init(void)
+{
+ os_callout_init(&no_transition_work, os_eventq_dflt_get(),
+ no_transition_work_handler, NULL);
+ os_callout_init(&save_lightness_temp_last_values_timer,
+ os_eventq_dflt_get(),
+ save_lightness_temp_last_values_timer_handler,
+ NULL);
+ os_callout_init(&unsolicitedly_publish_states_work, os_eventq_dflt_get(),
+ unsolicitedly_publish_states_work_handler, NULL);
+ os_callout_init(&unsolicitedly_publish_states_timer, os_eventq_dflt_get(),
+ unsolicitedly_publish_states_timer_handler, NULL);
+}
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.h
new file mode 100644
index 00000000..a747dfda
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _NO_TRANSITION_WORK_HANDLER_H
+#define _NO_TRANSITION_WORK_HANDLER_H
+
+extern struct os_callout no_transition_work;
+
+void no_transition_work_init(void);
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.c
new file mode 100644
index 00000000..21364b81
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.c
@@ -0,0 +1,266 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "console/console.h"
+#include "hal/hal_gpio.h"
+
+#include "app_gpio.h"
+
+#include "ble_mesh.h"
+#include "device_composition.h"
+#include "publisher.h"
+
+#define ONOFF
+#define GENERIC_LEVEL
+/* #define LIGHT_CTL */
+/* #define LIGHT_CTL_TEMP */
+
+static bool is_randomization_of_TIDs_done;
+
+#if (defined(ONOFF) || defined(ONOFF_TT))
+static u8_t tid_onoff;
+#elif defined(VND_MODEL_TEST)
+static u8_t tid_vnd;
+#endif
+
+static u8_t tid_level;
+
+void randomize_publishers_TID(void)
+{
+#if (defined(ONOFF) || defined(ONOFF_TT))
+ bt_rand(&tid_onoff, sizeof(tid_onoff));
+#elif defined(VND_MODEL_TEST)
+ bt_rand(&tid_vnd, sizeof(tid_vnd));
+#endif
+
+ bt_rand(&tid_level, sizeof(tid_level));
+
+ is_randomization_of_TIDs_done = true;
+}
+
+static u32_t button_read(int button)
+{
+ return (uint32_t) hal_gpio_read(button);
+}
+
+void publish(struct os_event *work)
+{
+ int err = 0;
+
+ if (is_randomization_of_TIDs_done == false) {
+ return;
+ }
+
+ if (button_read(button_device[0]) == 0) {
+#if defined(ONOFF)
+ bt_mesh_model_msg_init(root_models[3].pub->msg,
+ BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
+ net_buf_simple_add_u8(root_models[3].pub->msg, 0x01);
+ net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++);
+ err = bt_mesh_model_publish(&root_models[3]);
+#elif defined(ONOFF_TT)
+ bt_mesh_model_msg_init(root_models[3].pub->msg,
+ BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
+ net_buf_simple_add_u8(root_models[3].pub->msg, 0x01);
+ net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++);
+ net_buf_simple_add_u8(root_models[3].pub->msg, 0x45);
+ net_buf_simple_add_u8(root_models[3].pub->msg, 0x28);
+ err = bt_mesh_model_publish(&root_models[3]);
+#elif defined(VND_MODEL_TEST)
+ bt_mesh_model_msg_init(vnd_models[0].pub->msg,
+ BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME));
+ net_buf_simple_add_le16(vnd_models[0].pub->msg, 0x0001);
+ net_buf_simple_add_u8(vnd_models[0].pub->msg, tid_vnd++);
+ err = bt_mesh_model_publish(&vnd_models[0]);
+#endif
+
+ } else if (button_read(button_device[1]) == 0) {
+#if defined(ONOFF)
+ bt_mesh_model_msg_init(root_models[3].pub->msg,
+ BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
+ net_buf_simple_add_u8(root_models[3].pub->msg, 0x00);
+ net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++);
+ err = bt_mesh_model_publish(&root_models[3]);
+#elif defined(ONOFF_TT)
+ bt_mesh_model_msg_init(root_models[3].pub->msg,
+ BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
+ net_buf_simple_add_u8(root_models[3].pub->msg, 0x00);
+ net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++);
+ net_buf_simple_add_u8(root_models[3].pub->msg, 0x45);
+ net_buf_simple_add_u8(root_models[3].pub->msg, 0x28);
+ err = bt_mesh_model_publish(&root_models[3]);
+#elif defined(VND_MODEL_TEST)
+ bt_mesh_model_msg_init(vnd_models[0].pub->msg,
+ BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME));
+ net_buf_simple_add_le16(vnd_models[0].pub->msg, 0x0000);
+ net_buf_simple_add_u8(vnd_models[0].pub->msg, tid_vnd++);
+ err = bt_mesh_model_publish(&vnd_models[0]);
+#endif
+
+ } else if (button_read(button_device[2]) == 0) {
+#if defined(GENERIC_LEVEL)
+ bt_mesh_model_msg_init(root_models[5].pub->msg,
+ BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK);
+ net_buf_simple_add_le16(root_models[5].pub->msg, LEVEL_S25);
+ net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
+ err = bt_mesh_model_publish(&root_models[5]);
+#elif defined(ONOFF_GET)
+ bt_mesh_model_msg_init(root_models[3].pub->msg,
+ BT_MESH_MODEL_OP_GEN_ONOFF_GET);
+ err = bt_mesh_model_publish(&root_models[3]);
+#elif defined(GENERIC_DELTA_LEVEL)
+ bt_mesh_model_msg_init(root_models[5].pub->msg,
+ BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK);
+ net_buf_simple_add_le32(root_models[5].pub->msg, 100);
+ net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
+ err = bt_mesh_model_publish(&root_models[5]);
+#elif defined(GENERIC_MOVE_LEVEL_TT)
+ bt_mesh_model_msg_init(root_models[5].pub->msg,
+ BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK);
+ net_buf_simple_add_le16(root_models[5].pub->msg, 13100);
+ net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
+ net_buf_simple_add_u8(root_models[5].pub->msg, 0x45);
+ net_buf_simple_add_u8(root_models[5].pub->msg, 0x00);
+ err = bt_mesh_model_publish(&root_models[5]);
+#elif defined(LIGHT_LIGHTNESS_TT)
+ bt_mesh_model_msg_init(root_models[13].pub->msg,
+ BT_MESH_MODEL_OP_2(0x82, 0x4D));
+ net_buf_simple_add_le16(root_models[13].pub->msg, LEVEL_U25);
+ net_buf_simple_add_u8(root_models[13].pub->msg, tid_level++);
+ net_buf_simple_add_u8(root_models[13].pub->msg, 0x45);
+ net_buf_simple_add_u8(root_models[13].pub->msg, 0x28);
+ err = bt_mesh_model_publish(&root_models[13]);
+#elif defined(LIGHT_CTL)
+ bt_mesh_model_msg_init(root_models[16].pub->msg,
+ BT_MESH_MODEL_OP_2(0x82, 0x5F));
+ /* Lightness */
+ net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U25);
+ /* Temperature (value should be from 0x0320 to 0x4E20 */
+ /* This is as per 6.1.3.1 in Mesh Model Specification */
+ net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320);
+ /* Delta UV */
+ net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
+ net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
+ err = bt_mesh_model_publish(&root_models[16]);
+#elif defined(LIGHT_CTL_TT)
+ bt_mesh_model_msg_init(root_models[16].pub->msg,
+ BT_MESH_MODEL_OP_2(0x82, 0x5F));
+ /* Lightness */
+ net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U25);
+ /* Temperature (value should be from 0x0320 to 0x4E20 */
+ /* This is as per 6.1.3.1 in Mesh Model Specification */
+ net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320);
+ /* Delta UV */
+ net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
+ net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
+ net_buf_simple_add_u8(root_models[16].pub->msg, 0x45);
+ net_buf_simple_add_u8(root_models[16].pub->msg, 0x00);
+ err = bt_mesh_model_publish(&root_models[16]);
+#elif defined(LIGHT_CTL_TEMP)
+ bt_mesh_model_msg_init(root_models[16].pub->msg,
+ BT_MESH_MODEL_OP_2(0x82, 0x65));
+ /* Temperature (value should be from 0x0320 to 0x4E20 */
+ /* This is as per 6.1.3.1 in Mesh Model Specification */
+ net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320);
+ /* Delta UV */
+ net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
+ net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
+ err = bt_mesh_model_publish(&root_models[16]);
+#endif
+
+ } else if (button_read(button_device[3]) == 0) {
+#if defined(GENERIC_LEVEL)
+ bt_mesh_model_msg_init(root_models[5].pub->msg,
+ BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK);
+ net_buf_simple_add_le16(root_models[5].pub->msg, LEVEL_S100);
+ net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
+ err = bt_mesh_model_publish(&root_models[5]);
+#elif defined(GENERIC_DELTA_LEVEL)
+ bt_mesh_model_msg_init(root_models[5].pub->msg,
+ BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK);
+ net_buf_simple_add_le32(root_models[5].pub->msg, -100);
+ net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
+ err = bt_mesh_model_publish(&root_models[5]);
+#elif defined(GENERIC_MOVE_LEVEL_TT)
+ bt_mesh_model_msg_init(root_models[5].pub->msg,
+ BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK);
+ net_buf_simple_add_le16(root_models[5].pub->msg, -13100);
+ net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
+ net_buf_simple_add_u8(root_models[5].pub->msg, 0x45);
+ net_buf_simple_add_u8(root_models[5].pub->msg, 0x00);
+ err = bt_mesh_model_publish(&root_models[5]);
+#elif defined(LIGHT_LIGHTNESS_TT)
+ bt_mesh_model_msg_init(root_models[13].pub->msg,
+ BT_MESH_MODEL_OP_2(0x82, 0x4D));
+ net_buf_simple_add_le16(root_models[13].pub->msg, LEVEL_U100);
+ net_buf_simple_add_u8(root_models[13].pub->msg, tid_level++);
+ net_buf_simple_add_u8(root_models[13].pub->msg, 0x45);
+ net_buf_simple_add_u8(root_models[13].pub->msg, 0x28);
+ err = bt_mesh_model_publish(&root_models[13]);
+#elif defined(LIGHT_CTL)
+ bt_mesh_model_msg_init(root_models[16].pub->msg,
+ BT_MESH_MODEL_OP_2(0x82, 0x5F));
+ /* Lightness */
+ net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U100);
+ /* Temperature (value should be from 0x0320 to 0x4E20 */
+ /* This is as per 6.1.3.1 in Mesh Model Specification */
+ net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20);
+ /* Delta UV */
+ net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
+ net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
+ err = bt_mesh_model_publish(&root_models[16]);
+#elif defined(LIGHT_CTL_TT)
+ bt_mesh_model_msg_init(root_models[16].pub->msg,
+ BT_MESH_MODEL_OP_2(0x82, 0x5F));
+ /* Lightness */
+ net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U100);
+ /* Temperature (value should be from 0x0320 to 0x4E20 */
+ /* This is as per 6.1.3.1 in Mesh Model Specification */
+ net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20);
+ /* Delta UV */
+ net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
+ net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
+ net_buf_simple_add_u8(root_models[16].pub->msg, 0x45);
+ net_buf_simple_add_u8(root_models[16].pub->msg, 0x00);
+ err = bt_mesh_model_publish(&root_models[16]);
+#elif defined(LIGHT_CTL_TEMP)
+ bt_mesh_model_msg_init(root_models[16].pub->msg,
+ BT_MESH_MODEL_OP_2(0x82, 0x65));
+ /* Temperature (value should be from 0x0320 to 0x4E20 */
+ /* This is as per 6.1.3.1 in Mesh Model Specification */
+ net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20);
+ /* Delta UV */
+ net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
+ net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
+ err = bt_mesh_model_publish(&root_models[16]);
+#endif
+ }
+
+ if (err) {
+ printk("bt_mesh_model_publish: err: %d\n", err);
+ }
+}
+
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.h
new file mode 100644
index 00000000..09b740b4
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _PUBLISHER_H
+#define _PUBLISHER_H
+
+/* Others */
+#define LEVEL_S0 -32768
+#define LEVEL_S25 -16384
+#define LEVEL_S50 0
+#define LEVEL_S75 16384
+#define LEVEL_S100 32767
+
+#define LEVEL_U0 0
+#define LEVEL_U25 16384
+#define LEVEL_U50 32768
+#define LEVEL_U75 49152
+#define LEVEL_U100 65535
+
+void randomize_publishers_TID(void);
+void publish(struct os_event *work);
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.c
new file mode 100644
index 00000000..ae539433
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.c
@@ -0,0 +1,308 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <math.h>
+
+#include "ble_mesh.h"
+#include "device_composition.h"
+#include "state_binding.h"
+#include "transition.h"
+
+
+u16_t lightness, target_lightness;
+s16_t temperature, target_temperature;
+
+static s32_t ceiling(float num)
+{
+ s32_t inum;
+
+ inum = (s32_t) num;
+ if (num == (float) inum) {
+ return inum;
+ }
+
+ return inum + 1;
+}
+
+u16_t actual_to_linear(u16_t val)
+{
+ float tmp;
+
+ tmp = ((float) val / 65535);
+
+ return (u16_t) ceiling(65535 * tmp * tmp);
+}
+
+u16_t linear_to_actual(u16_t val)
+{
+ return (u16_t) (65535 * sqrt(((float) val / 65535)));
+}
+
+static void constrain_lightness(u16_t var)
+{
+ if (var > 0 && var < light_lightness_srv_user_data.light_range_min) {
+ var = light_lightness_srv_user_data.light_range_min;
+ } else if (var > light_lightness_srv_user_data.light_range_max) {
+ var = light_lightness_srv_user_data.light_range_max;
+ }
+
+ lightness = var;
+}
+
+static void constrain_lightness2(u16_t var)
+{
+ /* This is as per Mesh Model Specification 3.3.2.2.3 */
+ if (var > 0 && var < light_lightness_srv_user_data.light_range_min) {
+ if (gen_level_srv_root_user_data.last_delta < 0) {
+ var = 0U;
+ } else {
+ var = light_lightness_srv_user_data.light_range_min;
+ }
+ } else if (var > light_lightness_srv_user_data.light_range_max) {
+ var = light_lightness_srv_user_data.light_range_max;
+ }
+
+ lightness = var;
+}
+
+static void constrain_target_lightness(u16_t var)
+{
+ if (var > 0 &&
+ var < light_lightness_srv_user_data.light_range_min) {
+ var = light_lightness_srv_user_data.light_range_min;
+ } else if (var > light_lightness_srv_user_data.light_range_max) {
+ var = light_lightness_srv_user_data.light_range_max;
+ }
+
+ target_lightness = var;
+}
+
+static s16_t light_ctl_temp_to_level(u16_t temp)
+{
+ float tmp;
+
+ /* Mesh Model Specification 6.1.3.1.1 2nd formula start */
+
+ tmp = (temp - light_ctl_srv_user_data.temp_range_min) * 65535;
+
+ tmp = tmp / (light_ctl_srv_user_data.temp_range_max -
+ light_ctl_srv_user_data.temp_range_min);
+
+ return (s16_t) (tmp - 32768);
+
+ /* 6.1.3.1.1 2nd formula end */
+}
+
+static u16_t level_to_light_ctl_temp(s16_t level)
+{
+ u16_t tmp;
+ float diff;
+
+ /* Mesh Model Specification 6.1.3.1.1 1st formula start */
+ diff = (float) (light_ctl_srv_user_data.temp_range_max -
+ light_ctl_srv_user_data.temp_range_min) / 65535;
+
+
+ tmp = (u16_t) ((level + 32768) * diff);
+
+ return (light_ctl_srv_user_data.temp_range_min + tmp);
+
+ /* 6.1.3.1.1 1st formula end */
+}
+
+void state_binding(u8_t light, u8_t temp)
+{
+ switch (temp) {
+ case ONOFF_TEMP:
+ case CTL_TEMP:
+ temperature =
+ light_ctl_temp_to_level(light_ctl_srv_user_data.temp);
+
+ gen_level_srv_s0_user_data.level = temperature;
+ break;
+ case LEVEL_TEMP:
+ temperature = gen_level_srv_s0_user_data.level;
+ light_ctl_srv_user_data.temp =
+ level_to_light_ctl_temp(temperature);
+ break;
+ default:
+ break;
+ }
+
+ switch (light) {
+ case ONPOWERUP:
+ if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) {
+ lightness = 0U;
+ } else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) {
+ lightness = light_lightness_srv_user_data.last;
+ }
+ break;
+ case ONOFF:
+ if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) {
+ lightness = 0U;
+ } else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) {
+ if (light_lightness_srv_user_data.def == 0) {
+ lightness = light_lightness_srv_user_data.last;
+ } else {
+ lightness = light_lightness_srv_user_data.def;
+ }
+ }
+ break;
+ case LEVEL:
+ lightness = gen_level_srv_root_user_data.level + 32768;
+ break;
+ case DELTA_LEVEL:
+ lightness = gen_level_srv_root_user_data.level + 32768;
+ constrain_lightness2(lightness);
+ goto jump;
+ case ACTUAL:
+ lightness = light_lightness_srv_user_data.actual;
+ break;
+ case LINEAR:
+ lightness =
+ linear_to_actual(light_lightness_srv_user_data.linear);
+ break;
+ case CTL:
+ lightness = light_ctl_srv_user_data.lightness;
+ break;
+ default:
+ break;
+ }
+
+ constrain_lightness(lightness);
+
+jump:
+ if (lightness != 0) {
+ light_lightness_srv_user_data.last = lightness;
+ }
+
+ if (lightness) {
+ gen_onoff_srv_root_user_data.onoff = STATE_ON;
+ } else {
+ gen_onoff_srv_root_user_data.onoff = STATE_OFF;
+ }
+
+ gen_level_srv_root_user_data.level = lightness - 32768;
+ light_lightness_srv_user_data.actual = lightness;
+ light_lightness_srv_user_data.linear = actual_to_linear(lightness);
+ light_ctl_srv_user_data.lightness = lightness;
+}
+
+void calculate_lightness_target_values(u8_t type)
+{
+ bool set_light_ctl_temp_target_value;
+ u16_t tmp;
+
+ set_light_ctl_temp_target_value = true;
+
+ switch (type) {
+ case ONOFF:
+ if (gen_onoff_srv_root_user_data.target_onoff == 0) {
+ tmp = 0U;
+ } else {
+ if (light_lightness_srv_user_data.def == 0) {
+ tmp = light_lightness_srv_user_data.last;
+ } else {
+ tmp = light_lightness_srv_user_data.def;
+ }
+ }
+ break;
+ case LEVEL:
+ tmp = gen_level_srv_root_user_data.target_level + 32768;
+ break;
+ case ACTUAL:
+ tmp = light_lightness_srv_user_data.target_actual;
+ break;
+ case LINEAR:
+ tmp = linear_to_actual(light_lightness_srv_user_data.target_linear);
+ break;
+ case CTL:
+ set_light_ctl_temp_target_value = false;
+
+ tmp = light_ctl_srv_user_data.target_lightness;
+
+ target_temperature = light_ctl_temp_to_level(light_ctl_srv_user_data.target_temp);
+ gen_level_srv_s0_user_data.target_level = target_temperature;
+ break;
+ default:
+ return;
+ }
+
+ constrain_target_lightness(tmp);
+
+ if (target_lightness) {
+ gen_onoff_srv_root_user_data.target_onoff = STATE_ON;
+ } else {
+ gen_onoff_srv_root_user_data.target_onoff = STATE_OFF;
+ }
+
+ gen_level_srv_root_user_data.target_level = target_lightness - 32768;
+
+ light_lightness_srv_user_data.target_actual = target_lightness;
+
+ light_lightness_srv_user_data.target_linear =
+ actual_to_linear(target_lightness);
+
+ light_ctl_srv_user_data.target_lightness = target_lightness;
+
+ if (set_light_ctl_temp_target_value) {
+ target_temperature = light_ctl_srv_user_data.temp;
+ light_ctl_srv_user_data.target_temp = target_temperature;
+ }
+}
+
+void calculate_temp_target_values(u8_t type)
+{
+ bool set_light_ctl_delta_uv_target_value;
+
+ set_light_ctl_delta_uv_target_value = true;
+
+ switch (type) {
+ case LEVEL_TEMP:
+ target_temperature = gen_level_srv_s0_user_data.target_level;
+ light_ctl_srv_user_data.target_temp =
+ level_to_light_ctl_temp(target_temperature);
+ break;
+ case CTL_TEMP:
+ set_light_ctl_delta_uv_target_value = false;
+
+ target_temperature = light_ctl_temp_to_level(light_ctl_srv_user_data.target_temp);
+ gen_level_srv_s0_user_data.target_level = target_temperature;
+ break;
+ default:
+ return;
+ }
+
+ target_lightness = light_ctl_srv_user_data.lightness;
+ light_ctl_srv_user_data.target_lightness = target_lightness;
+
+ if (set_light_ctl_delta_uv_target_value) {
+
+ light_ctl_srv_user_data.target_delta_uv =
+ light_ctl_srv_user_data.delta_uv;
+ }
+}
+
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.h
new file mode 100644
index 00000000..db1f2a2e
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _STATE_BINDING_H
+#define _STATE_BINDING_H
+
+enum state_binding {
+ ONPOWERUP = 0x01,
+ ONOFF,
+ LEVEL,
+ DELTA_LEVEL,
+ ACTUAL,
+ LINEAR,
+ CTL,
+ IGNORE,
+
+ ONOFF_TEMP,
+ LEVEL_TEMP,
+ CTL_TEMP,
+ IGNORE_TEMP
+};
+
+extern u16_t lightness, target_lightness;
+extern s16_t temperature, target_temperature;
+
+void state_binding(u8_t lightness, u8_t temperature);
+void calculate_lightness_target_values(u8_t type);
+void calculate_temp_target_values(u8_t type);
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.c
new file mode 100644
index 00000000..86fec7cc
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.c
@@ -0,0 +1,255 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "base64/base64.h"
+#include "console/console.h"
+#include "mesh/mesh.h"
+
+#include "ble_mesh.h"
+#include "device_composition.h"
+#include "storage.h"
+
+static u8_t storage_id;
+u8_t reset_counter;
+
+static void save_reset_counter(void)
+{
+ char buf[5];
+
+ settings_str_from_bytes(&reset_counter, sizeof(reset_counter), buf,
+ sizeof(buf));
+
+ settings_save_one("ps/rc", buf);
+}
+
+static void save_gen_def_trans_time_state(void)
+{
+ char buf[5];
+
+ settings_str_from_bytes(&gen_def_trans_time_srv_user_data.tt,
+ sizeof(gen_def_trans_time_srv_user_data.tt),
+ buf, sizeof(buf));
+
+ settings_save_one("ps/gdtt", buf);
+}
+
+static void save_gen_onpowerup_state(void)
+{
+ char buf[5];
+
+ settings_str_from_bytes(&gen_power_onoff_srv_user_data.onpowerup,
+ sizeof(gen_power_onoff_srv_user_data.onpowerup),
+ buf, sizeof(buf));
+
+ settings_save_one("ps/gpo", buf);
+
+ if (gen_power_onoff_srv_user_data.onpowerup == 0x02) {
+ save_on_flash(LIGHTNESS_TEMP_LAST_STATE);
+ }
+}
+
+static void save_lightness_temp_def_state(void)
+{
+ char buf[12];
+
+ light_ctl_srv_user_data.lightness_temp_def =
+ (u32_t) ((light_ctl_srv_user_data.lightness_def << 16) |
+ light_ctl_srv_user_data.temp_def);
+
+ settings_str_from_bytes(&light_ctl_srv_user_data.lightness_temp_def,
+ sizeof(light_ctl_srv_user_data.lightness_temp_def),
+ buf, sizeof(buf));
+
+ settings_save_one("ps/ltd", buf);
+}
+
+static void save_lightness_temp_last_state(void)
+{
+ char buf[12];
+
+ light_ctl_srv_user_data.lightness_temp_last =
+ (u32_t) ((light_ctl_srv_user_data.lightness << 16) |
+ light_ctl_srv_user_data.temp);
+
+ settings_str_from_bytes(&light_ctl_srv_user_data.lightness_temp_last,
+ sizeof(light_ctl_srv_user_data.lightness_temp_last),
+ buf, sizeof(buf));
+
+ settings_save_one("ps/ltl", buf);
+
+ printk("Light CTL Last values have beed saved !!\n");
+}
+
+static void save_lightness_range(void)
+{
+ char buf[12];
+
+ light_lightness_srv_user_data.lightness_range =
+ (u32_t) ((light_lightness_srv_user_data.light_range_max << 16) |
+ light_lightness_srv_user_data.light_range_min);
+
+ settings_str_from_bytes(&light_lightness_srv_user_data.lightness_range,
+ sizeof(light_lightness_srv_user_data.lightness_range),
+ buf, sizeof(buf));
+
+ settings_save_one("ps/lr", buf);
+}
+
+static void save_temperature_range(void)
+{
+ char buf[12];
+
+ light_ctl_srv_user_data.temperature_range =
+ (u32_t) ((light_ctl_srv_user_data.temp_range_max << 16) |
+ light_ctl_srv_user_data.temp_range_min);
+
+ settings_str_from_bytes(&light_ctl_srv_user_data.temperature_range,
+ sizeof(light_ctl_srv_user_data.temperature_range),
+ buf, sizeof(buf));
+
+ settings_save_one("ps/tr", buf);
+}
+
+static void storage_work_handler(struct os_event *work)
+{
+ switch (storage_id) {
+ case RESET_COUNTER:
+ save_reset_counter();
+ break;
+ case GEN_DEF_TRANS_TIME_STATE:
+ save_gen_def_trans_time_state();
+ break;
+ case GEN_ONPOWERUP_STATE:
+ save_gen_onpowerup_state();
+ break;
+ case LIGHTNESS_TEMP_DEF_STATE:
+ save_lightness_temp_def_state();
+ break;
+ case LIGHTNESS_TEMP_LAST_STATE:
+ save_lightness_temp_last_state();
+ break;
+ case LIGHTNESS_RANGE:
+ save_lightness_range();
+ break;
+ case TEMPERATURE_RANGE:
+ save_temperature_range();
+ break;
+ }
+}
+
+struct os_callout storage_work;
+
+void save_on_flash(u8_t id)
+{
+ storage_id = id;
+ os_callout_reset(&storage_work, 0);
+}
+
+static int ps_set(int argc, char **argv, char *val)
+{
+ int len;
+
+ if (argc == 1) {
+ if (!strcmp(argv[0], "rc")) {
+ len = sizeof(reset_counter);
+
+ return settings_bytes_from_str(val, &reset_counter,
+ &len);
+ }
+
+ if (!strcmp(argv[0], "gdtt")) {
+ len = sizeof(gen_def_trans_time_srv_user_data.tt);
+
+ return settings_bytes_from_str(val,
+ &gen_def_trans_time_srv_user_data.tt, &len);
+ }
+
+ if (!strcmp(argv[0], "gpo")) {
+ len = sizeof(gen_power_onoff_srv_user_data.onpowerup);
+
+ return settings_bytes_from_str(val,
+ &gen_power_onoff_srv_user_data.onpowerup, &len);
+ }
+
+ if (!strcmp(argv[0], "ltd")) {
+ len = sizeof(light_ctl_srv_user_data.lightness_temp_def);
+
+ return settings_bytes_from_str(val,
+ &light_ctl_srv_user_data.lightness_temp_def,
+ &len);
+ }
+
+ if (!strcmp(argv[0], "ltl")) {
+ len = sizeof(light_ctl_srv_user_data.
+ lightness_temp_last);
+
+ return settings_bytes_from_str(val,
+ &light_ctl_srv_user_data.lightness_temp_last,
+ &len);
+ }
+
+ if (!strcmp(argv[0], "lr")) {
+ len = sizeof(light_lightness_srv_user_data.
+ lightness_range);
+
+ return settings_bytes_from_str(val,
+ &light_lightness_srv_user_data.lightness_range,
+ &len);
+ }
+
+ if (!strcmp(argv[0], "tr")) {
+ len = sizeof(light_ctl_srv_user_data.
+ temperature_range);
+
+ return settings_bytes_from_str(val,
+ &light_ctl_srv_user_data. temperature_range,
+ &len);
+ }
+ }
+
+ return -ENOENT;
+}
+
+static struct conf_handler ps_settings = {
+ .ch_name = "ps",
+ .ch_set = ps_set,
+};
+
+int ps_settings_init(void)
+{
+ int err;
+
+ os_callout_init(&storage_work, os_eventq_dflt_get(),
+ storage_work_handler, NULL);
+
+ err = conf_register(&ps_settings);
+ if (err) {
+ printk("ps_settings_register failed (err %d)", err);
+ return err;
+ }
+
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.h
new file mode 100644
index 00000000..e2905048
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _STORAGE_H
+#define _STORAGE_H
+
+enum ps_variables_id {
+ RESET_COUNTER = 0x01,
+ GEN_DEF_TRANS_TIME_STATE,
+ GEN_ONPOWERUP_STATE,
+ LIGHTNESS_TEMP_DEF_STATE,
+ LIGHTNESS_TEMP_LAST_STATE,
+ LIGHTNESS_RANGE,
+ TEMPERATURE_RANGE
+};
+
+extern u8_t reset_counter;
+
+extern struct os_callout storage_work;
+
+int ps_settings_init(void);
+void save_on_flash(u8_t id);
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.c
new file mode 100644
index 00000000..c9463e10
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.c
@@ -0,0 +1,792 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "ble_mesh.h"
+#include "common.h"
+#include "device_composition.h"
+#include "state_binding.h"
+#include "transition.h"
+
+struct os_callout onoff_work;
+struct os_callout level_lightness_work;
+struct os_callout level_temp_work;
+struct os_callout light_lightness_actual_work;
+struct os_callout light_lightness_linear_work;
+struct os_callout light_ctl_work;
+struct os_callout light_ctl_temp_work;
+
+struct os_callout dummy_timer;
+
+u8_t transition_type, default_tt;
+u32_t *ptr_counter;
+struct os_callout *ptr_timer = &dummy_timer;
+
+struct transition lightness_transition, temp_transition;
+
+/* Function to calculate Remaining Time (Start) */
+
+void calculate_rt(struct transition *transition)
+{
+ u8_t steps, resolution;
+ s32_t duration_remainder;
+ s64_t now;
+
+ if (transition->just_started) {
+ transition->rt = transition->tt;
+ } else {
+ now = k_uptime_get();
+ duration_remainder = transition->total_duration -
+ (now - transition->start_timestamp);
+
+ if (duration_remainder > 620000) {
+ /* > 620 seconds -> resolution = 0b11 [10 minutes] */
+ resolution = 0x03;
+ steps = duration_remainder / 600000;
+ } else if (duration_remainder > 62000) {
+ /* > 62 seconds -> resolution = 0b10 [10 seconds] */
+ resolution = 0x02;
+ steps = duration_remainder / 10000;
+ } else if (duration_remainder > 6200) {
+ /* > 6.2 seconds -> resolution = 0b01 [1 seconds] */
+ resolution = 0x01;
+ steps = duration_remainder / 1000;
+ } else if (duration_remainder > 0) {
+ /* <= 6.2 seconds -> resolution = 0b00 [100 ms] */
+ resolution = 0x00;
+ steps = duration_remainder / 100;
+ } else {
+ resolution = 0x00;
+ steps = 0x00;
+ }
+
+ transition->rt = (resolution << 6) | steps;
+ }
+}
+
+/* Function to calculate Remaining Time (End) */
+
+static void bound_states_transition_type_reassignment(u8_t type)
+{
+ switch (type) {
+ case ONOFF:
+ case LEVEL:
+ case ACTUAL:
+ case LINEAR:
+ light_ctl_srv_user_data.transition = &lightness_transition;
+ break;
+ case CTL:
+ light_ctl_srv_user_data.transition = &lightness_transition;
+ gen_level_srv_s0_user_data.transition = &lightness_transition;
+ break;
+ case LEVEL_TEMP:
+ case CTL_TEMP:
+ gen_level_srv_s0_user_data.transition = &temp_transition;
+ light_ctl_srv_user_data.transition = &temp_transition;
+ break;
+ default:
+ break;
+ }
+}
+
+static void tt_values_calculator(struct transition *transition)
+{
+ u8_t steps_multiplier, resolution;
+
+ resolution = (transition->tt >> 6);
+ steps_multiplier = (transition->tt & 0x3F);
+
+ switch (resolution) {
+ case 0: /* 100ms */
+ transition->total_duration = steps_multiplier * 100;
+ break;
+ case 1: /* 1 second */
+ transition->total_duration = steps_multiplier * 1000;
+ break;
+ case 2: /* 10 seconds */
+ transition->total_duration = steps_multiplier * 10000;
+ break;
+ case 3: /* 10 minutes */
+ transition->total_duration = steps_multiplier * 600000;
+ break;
+ }
+
+ transition->counter = ((float) transition->total_duration / 100);
+
+ if (transition->counter > DEVICE_SPECIFIC_RESOLUTION) {
+ transition->counter = DEVICE_SPECIFIC_RESOLUTION;
+ }
+
+ ptr_counter = &transition->counter;
+}
+
+void onoff_tt_values(struct generic_onoff_state *state, u8_t tt, u8_t delay)
+{
+ bound_states_transition_type_reassignment(ONOFF);
+ calculate_lightness_target_values(ONOFF);
+ state->transition->tt = tt;
+ state->transition->delay = delay;
+
+ if (tt != 0) {
+ tt_values_calculator(state->transition);
+ } else {
+ return;
+ }
+
+ state->transition->quo_tt = state->transition->total_duration /
+ state->transition->counter;
+
+ state->tt_delta = ((float) (lightness - target_lightness) /
+ state->transition->counter);
+}
+
+void level_tt_values(struct generic_level_state *state, u8_t tt, u8_t delay)
+{
+ if (state == &gen_level_srv_root_user_data) {
+ bound_states_transition_type_reassignment(LEVEL);
+ calculate_lightness_target_values(LEVEL);
+ } else if (state == &gen_level_srv_s0_user_data) {
+ bound_states_transition_type_reassignment(LEVEL_TEMP);
+ calculate_temp_target_values(LEVEL_TEMP);
+ }
+ state->transition->tt = tt;
+ state->transition->delay = delay;
+
+ if (tt != 0) {
+ tt_values_calculator(state->transition);
+ } else {
+ return;
+ }
+
+ state->transition->quo_tt = state->transition->total_duration /
+ state->transition->counter;
+
+ state->tt_delta = ((float) (state->level - state->target_level) /
+ state->transition->counter);
+}
+
+void light_lightness_actual_tt_values(struct light_lightness_state *state,
+ u8_t tt, u8_t delay)
+{
+ bound_states_transition_type_reassignment(ACTUAL);
+ calculate_lightness_target_values(ACTUAL);
+ state->transition->tt = tt;
+ state->transition->delay = delay;
+
+ if (tt != 0) {
+ tt_values_calculator(state->transition);
+ } else {
+ return;
+ }
+
+ state->transition->quo_tt = state->transition->total_duration /
+ state->transition->counter;
+
+ state->tt_delta_actual =
+ ((float) (state->actual - state->target_actual) /
+ state->transition->counter);
+}
+
+void light_lightness_linear_tt_values(struct light_lightness_state *state,
+ u8_t tt, u8_t delay)
+{
+ bound_states_transition_type_reassignment(LINEAR);
+ calculate_lightness_target_values(LINEAR);
+ state->transition->tt = tt;
+ state->transition->delay = delay;
+
+ if (tt != 0) {
+ tt_values_calculator(state->transition);
+ } else {
+ return;
+ }
+
+ state->transition->quo_tt = state->transition->total_duration /
+ state->transition->counter;
+
+ state->tt_delta_linear =
+ ((float) (state->linear - state->target_linear) /
+ state->transition->counter);
+}
+
+void light_ctl_tt_values(struct light_ctl_state *state, u8_t tt, u8_t delay)
+{
+ bound_states_transition_type_reassignment(CTL);
+ calculate_lightness_target_values(CTL);
+ state->transition->tt = tt;
+ state->transition->delay = delay;
+
+ if (tt != 0) {
+ tt_values_calculator(state->transition);
+ } else {
+ return;
+ }
+
+ state->transition->quo_tt = state->transition->total_duration /
+ state->transition->counter;
+
+ state->tt_delta_lightness =
+ ((float) (state->lightness - state->target_lightness) /
+ state->transition->counter);
+
+ state->tt_delta_temp =
+ ((float) (state->temp - state->target_temp) /
+ state->transition->counter);
+
+ state->tt_delta_duv =
+ ((float) (state->delta_uv - state->target_delta_uv) /
+ state->transition->counter);
+}
+
+void light_ctl_temp_tt_values(struct light_ctl_state *state,
+ u8_t tt, u8_t delay)
+{
+ bound_states_transition_type_reassignment(CTL_TEMP);
+ calculate_temp_target_values(CTL_TEMP);
+ state->transition->tt = tt;
+ state->transition->delay = delay;
+
+ if (tt != 0) {
+ tt_values_calculator(state->transition);
+ } else {
+ return;
+ }
+
+ state->transition->quo_tt = state->transition->total_duration /
+ state->transition->counter;
+
+ state->tt_delta_temp = ((float) (state->temp - state->target_temp) /
+ state->transition->counter);
+
+ state->tt_delta_duv =
+ ((float) (state->delta_uv - state->target_delta_uv) /
+ state->transition->counter);
+}
+
+/* Timers related handlers & threads (Start) */
+static void onoff_work_handler(struct os_event *work)
+{
+ struct generic_onoff_state *state = &gen_onoff_srv_root_user_data;
+
+ if (state->transition->just_started) {
+ state->transition->just_started = false;
+
+ if (state->transition->counter == 0) {
+ state_binding(ONOFF, IGNORE_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ } else {
+ state->transition->start_timestamp = k_uptime_get();
+
+ if (state->target_onoff == STATE_ON) {
+ state->onoff = STATE_ON;
+ }
+ }
+
+ return;
+ }
+
+ if (state->transition->counter != 0) {
+ state->transition->counter--;
+
+ lightness -= state->tt_delta;
+
+ state_binding(IGNORE, IGNORE_TEMP);
+ update_light_state();
+ }
+
+ if (state->transition->counter == 0) {
+ state->onoff = state->target_onoff;
+ lightness = target_lightness;
+
+ state_binding(IGNORE, IGNORE_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ }
+}
+
+static void level_lightness_work_handler(struct os_event *work)
+{
+ u8_t level;
+ struct generic_level_state *state = &gen_level_srv_root_user_data;
+
+ switch (transition_type) {
+ case LEVEL_TT:
+ level = LEVEL;
+ break;
+ case LEVEL_TT_DELTA:
+ level = DELTA_LEVEL;
+ break;
+ case LEVEL_TT_MOVE:
+ level = LEVEL;
+ break;
+ default:
+ return;
+ }
+
+ if (state->transition->just_started) {
+ state->transition->just_started = false;
+
+ if (state->transition->counter == 0) {
+ state_binding(level, IGNORE_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ } else {
+ state->transition->start_timestamp = k_uptime_get();
+ }
+
+ return;
+ }
+
+ if (state->transition->counter != 0) {
+ state->transition->counter--;
+
+ state->level -= state->tt_delta;
+
+ state_binding(level, IGNORE_TEMP);
+ update_light_state();
+ }
+
+ if (state->transition->counter == 0) {
+ state->level = state->target_level;
+
+ state_binding(level, IGNORE_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ }
+}
+
+static void level_temp_work_handler(struct os_event *work)
+{
+ struct generic_level_state *state = &gen_level_srv_s0_user_data;
+
+ switch (transition_type) {
+ case LEVEL_TEMP_TT:
+ break;
+ case LEVEL_TEMP_TT_DELTA:
+ break;
+ case LEVEL_TEMP_TT_MOVE:
+ break;
+ default:
+ return;
+ }
+
+ if (state->transition->just_started) {
+ state->transition->just_started = false;
+
+ if (state->transition->counter == 0) {
+ state_binding(IGNORE, LEVEL_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ } else {
+ state->transition->start_timestamp = k_uptime_get();
+ }
+
+ return;
+ }
+
+ if (state->transition->counter != 0) {
+ state->transition->counter--;
+
+ state->level -= state->tt_delta;
+
+ state_binding(IGNORE, LEVEL_TEMP);
+ update_light_state();
+ }
+
+ if (state->transition->counter == 0) {
+ state->level = state->target_level;
+
+ state_binding(IGNORE, LEVEL_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ }
+}
+
+static void light_lightness_actual_work_handler(struct os_event *work)
+{
+ struct light_lightness_state *state = &light_lightness_srv_user_data;
+
+ if (state->transition->just_started) {
+ state->transition->just_started = false;
+
+ if (state->transition->counter == 0) {
+ state_binding(ACTUAL, IGNORE_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ } else {
+ state->transition->start_timestamp = k_uptime_get();
+ }
+
+ return;
+ }
+
+ if (state->transition->counter != 0) {
+ state->transition->counter--;
+
+ state->actual -= state->tt_delta_actual;
+
+ state_binding(ACTUAL, IGNORE_TEMP);
+ update_light_state();
+ }
+
+ if (state->transition->counter == 0) {
+ state->actual = state->target_actual;
+
+ state_binding(ACTUAL, IGNORE_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ }
+}
+
+static void light_lightness_linear_work_handler(struct os_event *work)
+{
+ struct light_lightness_state *state = &light_lightness_srv_user_data;
+
+ if (state->transition->just_started) {
+ state->transition->just_started = false;
+
+ if (state->transition->counter == 0) {
+ state_binding(LINEAR, IGNORE_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ } else {
+ state->transition->start_timestamp = k_uptime_get();
+ }
+
+ return;
+ }
+
+ if (state->transition->counter != 0) {
+ state->transition->counter--;
+
+ state->linear -= state->tt_delta_linear;
+
+ state_binding(LINEAR, IGNORE_TEMP);
+ update_light_state();
+ }
+
+ if (state->transition->counter == 0) {
+ state->linear = state->target_linear;
+
+ state_binding(LINEAR, IGNORE_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ }
+}
+
+static void light_ctl_work_handler(struct os_event *work)
+{
+ struct light_ctl_state *state = &light_ctl_srv_user_data;
+
+ if (state->transition->just_started) {
+ state->transition->just_started = false;
+
+ if (state->transition->counter == 0) {
+ state_binding(CTL, CTL_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ } else {
+ state->transition->start_timestamp = k_uptime_get();
+ }
+
+ return;
+ }
+
+ if (state->transition->counter != 0) {
+ state->transition->counter--;
+
+ /* Lightness */
+ state->lightness -= state->tt_delta_lightness;
+
+ /* Temperature */
+ state->temp -= state->tt_delta_temp;
+
+ /* Delta_UV */
+ state->delta_uv -= state->tt_delta_duv;
+
+ state_binding(CTL, CTL_TEMP);
+ update_light_state();
+ }
+
+ if (state->transition->counter == 0) {
+ state->lightness = state->target_lightness;
+ state->temp = state->target_temp;
+ state->delta_uv = state->target_delta_uv;
+
+ state_binding(CTL, CTL_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ }
+}
+
+static void light_ctl_temp_work_handler(struct os_event *work)
+{
+ struct light_ctl_state *state = &light_ctl_srv_user_data;
+
+ if (state->transition->just_started) {
+ state->transition->just_started = false;
+
+ if (state->transition->counter == 0) {
+ state_binding(IGNORE, CTL_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ } else {
+ state->transition->start_timestamp = k_uptime_get();
+ }
+
+ return;
+ }
+
+ if (state->transition->counter != 0) {
+ state->transition->counter--;
+
+ /* Temperature */
+ state->temp -= state->tt_delta_temp;
+
+ /* Delta UV */
+ state->delta_uv -= state->tt_delta_duv;
+
+ state_binding(IGNORE, CTL_TEMP);
+ update_light_state();
+ }
+
+ if (state->transition->counter == 0) {
+ state->temp = state->target_temp;
+ state->delta_uv = state->target_delta_uv;
+
+ state_binding(IGNORE, CTL_TEMP);
+ update_light_state();
+
+ os_callout_stop(ptr_timer);
+ }
+}
+
+static void dummy_timer_handler(struct os_event *ev)
+{ }
+
+static void onoff_tt_handler(struct os_event *ev)
+{
+ struct generic_onoff_state *state = ev->ev_arg;
+
+ assert(state != NULL);
+ os_callout_reset(&onoff_work, 0);
+ os_callout_reset(&state->transition->timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(state->transition->quo_tt)));
+}
+
+static void level_lightness_tt_handler(struct os_event *ev)
+{
+ struct generic_level_state *state = ev->ev_arg;
+
+ assert(state != NULL);
+ os_callout_reset(&level_lightness_work, 0);
+ os_callout_reset(&state->transition->timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(state->transition->quo_tt)));
+}
+
+static void level_temp_tt_handler(struct os_event *ev)
+{
+ struct generic_level_state *state = ev->ev_arg;
+
+ assert(state != NULL);
+ os_callout_reset(&level_temp_work, 0);
+ os_callout_reset(&state->transition->timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(state->transition->quo_tt)));
+}
+
+static void light_lightness_actual_tt_handler(struct os_event *ev)
+{
+ struct light_lightness_state *state = ev->ev_arg;
+
+ assert(state != NULL);
+ os_callout_reset(&light_lightness_actual_work, 0);
+ os_callout_reset(&state->transition->timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(state->transition->quo_tt)));
+}
+
+static void light_lightness_linear_tt_handler(struct os_event *ev)
+{
+ struct light_lightness_state *state = ev->ev_arg;
+
+ assert(state != NULL);
+ os_callout_reset(&light_lightness_linear_work, 0);
+ os_callout_reset(&state->transition->timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(state->transition->quo_tt)));
+}
+
+static void light_ctl_tt_handler(struct os_event *ev)
+{
+ struct light_ctl_state *state = ev->ev_arg;
+
+ assert(state != NULL);
+ os_callout_reset(&light_ctl_work, 0);
+ os_callout_reset(&state->transition->timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(state->transition->quo_tt)));
+}
+
+static void light_ctl_temp_tt_handler(struct os_event *ev)
+{
+ struct light_ctl_state *state = ev->ev_arg;
+
+ assert(state != NULL);
+ os_callout_reset(&light_ctl_temp_work, 0);
+ os_callout_reset(&state->transition->timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(state->transition->quo_tt)));
+}
+/* Timers related handlers & threads (End) */
+
+/* Messages handlers (Start) */
+void onoff_handler(struct generic_onoff_state *state)
+{
+ ptr_timer = &state->transition->timer;
+
+ os_callout_init(ptr_timer, os_eventq_dflt_get(),
+ onoff_tt_handler, NULL);
+ ptr_timer->c_ev.ev_arg = state;
+ os_callout_reset(ptr_timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(5 * state->transition->delay)));
+}
+
+void level_lightness_handler(struct generic_level_state *state)
+{
+ ptr_timer = &state->transition->timer;
+
+ os_callout_init(ptr_timer, os_eventq_dflt_get(),
+ level_lightness_tt_handler, NULL);
+ ptr_timer->c_ev.ev_arg = state;
+ os_callout_reset(ptr_timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(5 * state->transition->delay)));
+}
+
+void level_temp_handler(struct generic_level_state *state)
+{
+ ptr_timer = &state->transition->timer;
+
+ os_callout_init(ptr_timer, os_eventq_dflt_get(),
+ level_temp_tt_handler, NULL);
+ ptr_timer->c_ev.ev_arg = state;
+ os_callout_reset(ptr_timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(5 * state->transition->delay)));
+}
+
+void light_lightness_actual_handler(struct light_lightness_state *state)
+{
+ ptr_timer = &state->transition->timer;
+
+ os_callout_init(ptr_timer, os_eventq_dflt_get(),
+ light_lightness_actual_tt_handler, NULL);
+ ptr_timer->c_ev.ev_arg = state;
+ os_callout_reset(ptr_timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(5 * state->transition->delay)));
+}
+
+void light_lightness_linear_handler(struct light_lightness_state *state)
+{
+ ptr_timer = &state->transition->timer;
+
+ os_callout_init(ptr_timer, os_eventq_dflt_get(),
+ light_lightness_linear_tt_handler, NULL);
+ ptr_timer->c_ev.ev_arg = state;
+ os_callout_reset(ptr_timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(5 * state->transition->delay)));
+}
+
+void light_ctl_handler(struct light_ctl_state *state)
+{
+ ptr_timer = &state->transition->timer;
+
+ os_callout_init(ptr_timer, os_eventq_dflt_get(),
+ light_ctl_tt_handler, NULL);
+ ptr_timer->c_ev.ev_arg = state;
+ os_callout_reset(ptr_timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(5 * state->transition->delay)));
+}
+
+void light_ctl_temp_handler(struct light_ctl_state *state)
+{
+ ptr_timer = &state->transition->timer;
+
+ os_callout_init(ptr_timer, os_eventq_dflt_get(),
+ light_ctl_temp_tt_handler, NULL);
+ ptr_timer->c_ev.ev_arg = state;
+ os_callout_reset(ptr_timer,
+ os_time_ms_to_ticks32(
+ K_MSEC(5 * state->transition->delay)));
+}
+/* Messages handlers (End) */
+
+void transition_timers_init(void)
+{
+ os_callout_init(&onoff_work, os_eventq_dflt_get(),
+ onoff_work_handler, NULL);
+
+ os_callout_init(&level_lightness_work, os_eventq_dflt_get(),
+ level_lightness_work_handler, NULL);
+ os_callout_init(&level_temp_work, os_eventq_dflt_get(),
+ level_temp_work_handler, NULL);
+
+ os_callout_init(&light_lightness_actual_work,
+ os_eventq_dflt_get(),
+ light_lightness_actual_work_handler, NULL);
+ os_callout_init(&light_lightness_linear_work,
+ os_eventq_dflt_get(),
+ light_lightness_linear_work_handler, NULL);
+
+ os_callout_init(&light_ctl_work, os_eventq_dflt_get(),
+ light_ctl_work_handler, NULL);
+ os_callout_init(&light_ctl_temp_work, os_eventq_dflt_get(),
+ light_ctl_temp_work_handler, NULL);
+
+ os_callout_init(&dummy_timer, os_eventq_dflt_get(),
+ dummy_timer_handler, NULL);
+}
+
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.h
new file mode 100644
index 00000000..84101395
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.h
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _TRANSITION_H
+#define _TRANSITION_H
+
+#define UNKNOWN_VALUE 0x3F
+#define DEVICE_SPECIFIC_RESOLUTION 10
+
+enum level_transition_types {
+ LEVEL_TT,
+ LEVEL_TT_DELTA,
+ LEVEL_TT_MOVE,
+
+ LEVEL_TEMP_TT,
+ LEVEL_TEMP_TT_DELTA,
+ LEVEL_TEMP_TT_MOVE,
+};
+
+struct transition {
+ bool just_started;
+ u8_t tt;
+ u8_t rt;
+ u8_t delay;
+ u32_t quo_tt;
+ u32_t counter;
+ u32_t total_duration;
+ s64_t start_timestamp;
+
+ struct os_callout timer;
+};
+
+extern u8_t transition_type, default_tt;
+extern u32_t *ptr_counter;
+extern struct os_callout *ptr_timer;
+
+extern struct transition lightness_transition, temp_transition;
+
+extern struct os_callout dummy_timer;
+
+void calculate_rt(struct transition *transition);
+
+
+void onoff_tt_values(struct generic_onoff_state *state, u8_t tt, u8_t delay);
+void level_tt_values(struct generic_level_state *state, u8_t tt, u8_t delay);
+void light_lightness_actual_tt_values(struct light_lightness_state *state,
+ u8_t tt, u8_t delay);
+void light_lightness_linear_tt_values(struct light_lightness_state *state,
+ u8_t tt, u8_t delay);
+void light_ctl_tt_values(struct light_ctl_state *state, u8_t tt, u8_t delay);
+void light_ctl_temp_tt_values(struct light_ctl_state *state,
+ u8_t tt, u8_t delay);
+
+void onoff_handler(struct generic_onoff_state *state);
+void level_lightness_handler(struct generic_level_state *state);
+void level_temp_handler(struct generic_level_state *state);
+void light_lightness_actual_handler(struct light_lightness_state *state);
+void light_lightness_linear_handler(struct light_lightness_state *state);
+void light_ctl_handler(struct light_ctl_state *state);
+void light_ctl_temp_handler(struct light_ctl_state *state);
+
+void transition_timers_init(void);
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/syscfg.yml b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/syscfg.yml
new file mode 100644
index 00000000..b56e9d2d
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/syscfg.yml
@@ -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.
+#
+
+syscfg.vals:
+ # Set log level to info (disable debug logging).
+ LOG_LEVEL: 1
+
+ # Default task settings
+ OS_MAIN_STACK_SIZE: 4096
+
+ # SMP is not supported in this app, so disable smp-over-shell.
+ SHELL_MGMT: 0
+
+ MSYS_1_BLOCK_COUNT: 48
+
+ FLOAT_USER: 1
+ HARD_FLOAT: 1
+
+ BLE_MESH_DEV_UUID: "((uint8_t[16]){0xdd, 0xdd, 0})"
+ BLE_MESH_ADV_BUF_COUNT: 60
+ BLE_MESH_TX_SEG_MAX: 6
+ BLE_MESH_TX_SEG_MSG_COUNT: 3
+ BLE_MESH_RX_SEG_MSG_COUNT: 3
+ BLE_MESH_CRPL: 128
+ BLE_MESH_RPL_STORE_TIMEOUT: 120
+ BLE_MESH_MSG_CACHE_SIZE: 100
+
+ BLE_MESH_SETTINGS: 1
+ CONFIG_FCB: 1
+
+ BLE_MESH: 1
+ BLE_MESH_RELAY: 1
+ BLE_MESH_LOW_POWER: 0
+ BLE_MESH_LPN_AUTO: 0
+ BLE_MESH_FRIEND: 0
+
+ BLE_MESH_PROV: 1
+ BLE_MESH_PB_ADV: 1
+ BLE_MESH_PB_GATT: 1
+ BLE_MESH_GATT_PROXY: 1
+
+ BLE_MESH_SUBNET_COUNT: 2
+ BLE_MESH_APP_KEY_COUNT: 2
+ BLE_MESH_MODEL_GROUP_COUNT: 2
+ BLE_MESH_LABEL_COUNT: 3
diff --git a/src/libs/mynewt-nimble/apps/blemesh_shell/pkg.yml b/src/libs/mynewt-nimble/apps/blemesh_shell/pkg.yml
new file mode 100644
index 00000000..ccf43be1
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_shell/pkg.yml
@@ -0,0 +1,37 @@
+# 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/blemesh_shell
+pkg.type: app
+pkg.description: Sample application for BLE Mesh node with shell support
+pkg.author: "Michał Narajowski <michal.narajowski@codecoup.pl>"
+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"
+ - "@apache-mynewt-core/sys/shell"
+ - nimble/controller
+ - nimble/host
+ - nimble/host/services/gap
+ - nimble/host/services/gatt
+ - nimble/host/store/ram
+ - nimble/transport/ram
diff --git a/src/libs/mynewt-nimble/apps/blemesh_shell/src/main.c b/src/libs/mynewt-nimble/apps/blemesh_shell/src/main.c
new file mode 100644
index 00000000..4ad23e1d
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_shell/src/main.c
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+#define MESH_LOG_MODULE BLE_MESH_LOG
+
+#include <assert.h>
+#include "os/mynewt.h"
+#include "mesh/mesh.h"
+#include "console/console.h"
+#include "hal/hal_system.h"
+#include "hal/hal_gpio.h"
+#include "bsp/bsp.h"
+#include "shell/shell.h"
+
+/* BLE */
+#include "nimble/ble.h"
+#include "host/ble_hs.h"
+#include "services/gap/ble_svc_gap.h"
+#include "mesh/glue.h"
+#include "mesh/testing.h"
+
+
+void net_recv_ev(uint8_t ttl, uint8_t ctl, uint16_t src, uint16_t dst,
+ const void *payload, size_t payload_len)
+{
+ console_printf("Received net packet: ttl 0x%02x ctl 0x%02x src 0x%04x "
+ "dst 0x%04x " "payload_len %d\n", ttl, ctl, src, dst,
+ payload_len);
+}
+
+static void model_bound_cb(u16_t addr, struct bt_mesh_model *model,
+ u16_t key_idx)
+{
+ console_printf("Model bound: remote addr 0x%04x key_idx 0x%04x model %p\n",
+ addr, key_idx, model);
+}
+
+static void model_unbound_cb(u16_t addr, struct bt_mesh_model *model,
+ u16_t key_idx)
+{
+ console_printf("Model unbound: remote addr 0x%04x key_idx 0x%04x "
+ "model %p\n", addr, key_idx, model);
+}
+
+static void invalid_bearer_cb(u8_t opcode)
+{
+ console_printf("Invalid bearer: opcode 0x%02x\n", opcode);
+}
+
+static void incomp_timer_exp_cb(void)
+{
+ console_printf("Incomplete timer expired\n");
+}
+
+static struct bt_test_cb bt_test_cb = {
+ .mesh_net_recv = net_recv_ev,
+ .mesh_model_bound = model_bound_cb,
+ .mesh_model_unbound = model_unbound_cb,
+ .mesh_prov_invalid_bearer = invalid_bearer_cb,
+ .mesh_trans_incomp_timer_exp = incomp_timer_exp_cb,
+};
+
+static void
+blemesh_on_reset(int reason)
+{
+ BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
+}
+
+static void
+blemesh_on_sync(void)
+{
+ console_printf("Bluetooth initialized\n");
+
+ shell_register_default_module("mesh");
+
+ if (IS_ENABLED(CONFIG_BT_TESTING)) {
+ bt_test_cb_register(&bt_test_cb);
+ }
+}
+
+int
+main(void)
+{
+ /* Initialize OS */
+ sysinit();
+
+ /* Initialize the NimBLE host configuration. */
+ ble_hs_cfg.reset_cb = blemesh_on_reset;
+ ble_hs_cfg.sync_cb = blemesh_on_sync;
+ ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
+
+ hal_gpio_init_out(LED_2, 0);
+
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/blemesh_shell/syscfg.yml b/src/libs/mynewt-nimble/apps/blemesh_shell/syscfg.yml
new file mode 100644
index 00000000..cbfd0c59
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blemesh_shell/syscfg.yml
@@ -0,0 +1,57 @@
+# 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:
+ # Enable the shell task.
+ SHELL_TASK: 1
+
+ # Set log level to info (disable debug logging).
+ LOG_LEVEL: 0
+
+ # Default task settings
+ OS_MAIN_STACK_SIZE: 768
+
+ # SMP is not supported in this app, so disable smp-over-shell.
+ SHELL_MGMT: 0
+
+ MSYS_1_BLOCK_COUNT: 80
+
+ BLE_MESH_ADV_BUF_COUNT: 20
+ BLE_MESH_TX_SEG_MAX: 6
+
+ BLE_MESH: 1
+ BLE_MESH_SHELL: 1
+ BLE_MESH_PROV: 1
+ BLE_MESH_PROVISIONER: 1
+ BLE_MESH_RELAY: 1
+ BLE_MESH_PB_ADV: 1
+ BLE_MESH_PB_GATT: 1
+ BLE_MESH_LOW_POWER: 1
+ BLE_MESH_LPN_AUTO: 0
+ BLE_MESH_GATT_PROXY: 1
+ BLE_MESH_LABEL_COUNT: 2
+ BLE_MESH_SUBNET_COUNT: 2
+ BLE_MESH_MODEL_GROUP_COUNT: 2
+ BLE_MESH_MODEL_EXTENSIONS: 1
+ BLE_MESH_APP_KEY_COUNT: 4
+ BLE_MESH_IV_UPDATE_TEST: 1
+ BLE_MESH_TESTING: 1
+ BLE_MESH_FRIEND: 1
+ BLE_MESH_CFG_CLI: 1
+ BLE_MESH_SETTINGS: 0
+ CONFIG_NFFS: 0
diff --git a/src/libs/mynewt-nimble/apps/bleprph/pkg.yml b/src/libs/mynewt-nimble/apps/bleprph/pkg.yml
new file mode 100644
index 00000000..38b6e445
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bleprph/pkg.yml
@@ -0,0 +1,45 @@
+# 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/bleprph
+pkg.type: app
+pkg.description: Simple BLE peripheral application.
+pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+ - "@apache-mynewt-core/boot/split"
+ - "@mcuboot/boot/bootutil"
+ - "@apache-mynewt-core/kernel/os"
+ - "@apache-mynewt-core/mgmt/imgmgr"
+ - "@apache-mynewt-core/mgmt/smp"
+ - "@apache-mynewt-core/mgmt/smp/transport/ble"
+ - "@apache-mynewt-core/sys/console/full"
+ - "@apache-mynewt-core/sys/log/full"
+ - "@apache-mynewt-core/sys/log/modlog"
+ - "@apache-mynewt-core/sys/stats/full"
+ - "@apache-mynewt-core/sys/sysinit"
+ - "@apache-mynewt-core/sys/id"
+ - nimble/host
+ - nimble/host/services/ans
+ - nimble/host/services/dis
+ - nimble/host/services/gap
+ - nimble/host/services/gatt
+ - nimble/host/store/config
+ - nimble/host/util
+ - nimble/transport
diff --git a/src/libs/mynewt-nimble/apps/bleprph/src/bleprph.h b/src/libs/mynewt-nimble/apps/bleprph/src/bleprph.h
new file mode 100644
index 00000000..5ec34610
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bleprph/src/bleprph.h
@@ -0,0 +1,61 @@
+/*
+ * 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 <stdbool.h>
+#include "nimble/ble.h"
+#include "modlog/modlog.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ble_hs_cfg;
+struct ble_gatt_register_ctxt;
+
+/** 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);
+
+/* PHY support */
+#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
+#define CONN_HANDLE_INVALID 0xffff
+
+void phy_init(void);
+void phy_conn_changed(uint16_t handle);
+void phy_update(uint8_t phy);
+#endif
+
+/** Misc. */
+void print_bytes(const uint8_t *bytes, int len);
+void print_addr(const void *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/bleprph/src/gatt_svr.c b/src/libs/mynewt-nimble/apps/bleprph/src/gatt_svr.c
new file mode 100644
index 00000000..632ef4fb
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bleprph/src/gatt_svr.c
@@ -0,0 +1,204 @@
+/*
+ * 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 "bleprph.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.
+ */
+
+/* 59462f12-9543-9999-12c8-58b459a2712d */
+static const ble_uuid128_t gatt_svr_svc_sec_test_uuid =
+ BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12,
+ 0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59);
+
+/* 5c3a659e-897e-45e1-b016-007107c96df6 */
+static const ble_uuid128_t gatt_svr_chr_sec_test_rand_uuid =
+ BLE_UUID128_INIT(0xf6, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
+ 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
+
+/* 5c3a659e-897e-45e1-b016-007107c96df7 */
+static const ble_uuid128_t gatt_svr_chr_sec_test_static_uuid =
+ BLE_UUID128_INIT(0xf7, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
+ 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
+
+static uint8_t gatt_svr_sec_test_static_val;
+
+static int
+gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
+ {
+ /*** Service: Security test. */
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = &gatt_svr_svc_sec_test_uuid.u,
+ .characteristics = (struct ble_gatt_chr_def[]) { {
+ /*** Characteristic: Random number generator. */
+ .uuid = &gatt_svr_chr_sec_test_rand_uuid.u,
+ .access_cb = gatt_svr_chr_access_sec_test,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC,
+ }, {
+ /*** Characteristic: Static value. */
+ .uuid = &gatt_svr_chr_sec_test_static_uuid.u,
+ .access_cb = gatt_svr_chr_access_sec_test,
+ .flags = BLE_GATT_CHR_F_READ |
+ BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC,
+ }, {
+ 0, /* No more characteristics in this service. */
+ } },
+ },
+
+ {
+ 0, /* No more services. */
+ },
+};
+
+static int
+gatt_svr_chr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len,
+ void *dst, uint16_t *len)
+{
+ uint16_t om_len;
+ int rc;
+
+ om_len = OS_MBUF_PKTLEN(om);
+ if (om_len < min_len || om_len > max_len) {
+ return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
+ }
+
+ rc = ble_hs_mbuf_to_flat(om, dst, max_len, len);
+ if (rc != 0) {
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+
+ return 0;
+}
+
+static int
+gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ const ble_uuid_t *uuid;
+ int rand_num;
+ int rc;
+
+ uuid = ctxt->chr->uuid;
+
+ /* Determine which characteristic is being accessed by examining its
+ * 128-bit UUID.
+ */
+
+ if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_rand_uuid.u) == 0) {
+ assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
+
+ /* Respond with a 32-bit random number. */
+ rand_num = rand();
+ rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_uuid.u) == 0) {
+ switch (ctxt->op) {
+ case BLE_GATT_ACCESS_OP_READ_CHR:
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_sec_test_static_val,
+ sizeof gatt_svr_sec_test_static_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+
+ case BLE_GATT_ACCESS_OP_WRITE_CHR:
+ rc = gatt_svr_chr_write(ctxt->om,
+ sizeof gatt_svr_sec_test_static_val,
+ sizeof gatt_svr_sec_test_static_val,
+ &gatt_svr_sec_test_static_val, NULL);
+ return rc;
+
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+ }
+
+ /* Unknown characteristic; the nimble stack should not have called this
+ * function.
+ */
+ assert(0);
+ 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:
+ MODLOG_DFLT(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:
+ MODLOG_DFLT(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:
+ MODLOG_DFLT(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;
+}
diff --git a/src/libs/mynewt-nimble/apps/bleprph/src/main.c b/src/libs/mynewt-nimble/apps/bleprph/src/main.c
new file mode 100644
index 00000000..66f9bacc
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bleprph/src/main.c
@@ -0,0 +1,359 @@
+/*
+ * 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 "os/mynewt.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"
+#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0
+#include "bootutil/image.h"
+#include "imgmgr/imgmgr.h"
+#include "services/dis/ble_svc_dis.h"
+#endif
+
+/* BLE */
+#include "nimble/ble.h"
+#include "host/ble_hs.h"
+#include "host/util/util.h"
+#include "services/gap/ble_svc_gap.h"
+
+/* Application-specified header. */
+#include "bleprph.h"
+
+static int bleprph_gap_event(struct ble_gap_event *event, void *arg);
+
+/**
+ * Logs information about a connection to the console.
+ */
+static void
+bleprph_print_conn_desc(struct ble_gap_conn_desc *desc)
+{
+ MODLOG_DFLT(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);
+ MODLOG_DFLT(INFO, " our_id_addr_type=%d our_id_addr=",
+ desc->our_id_addr.type);
+ print_addr(desc->our_id_addr.val);
+ MODLOG_DFLT(INFO, " peer_ota_addr_type=%d peer_ota_addr=",
+ desc->peer_ota_addr.type);
+ print_addr(desc->peer_ota_addr.val);
+ MODLOG_DFLT(INFO, " peer_id_addr_type=%d peer_id_addr=",
+ desc->peer_id_addr.type);
+ print_addr(desc->peer_id_addr.val);
+ MODLOG_DFLT(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 Undirected connectable mode.
+ */
+static void
+bleprph_advertise(void)
+{
+ uint8_t own_addr_type;
+ struct ble_gap_adv_params adv_params;
+ struct ble_hs_adv_fields fields;
+ const char *name;
+ 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;
+ }
+
+ /**
+ * 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) {
+ MODLOG_DFLT(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_UND;
+ adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
+ rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER,
+ &adv_params, bleprph_gap_event, NULL);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
+ return;
+ }
+}
+
+/**
+ * The nimble host executes this callback when a GAP event occurs. The
+ * application associates a GAP event callback with each connection that forms.
+ * bleprph 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
+ * bleprph.
+ *
+ * @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
+bleprph_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. */
+ MODLOG_DFLT(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);
+ bleprph_print_conn_desc(&desc);
+
+#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
+ phy_conn_changed(event->connect.conn_handle);
+#endif
+ }
+ MODLOG_DFLT(INFO, "\n");
+
+ if (event->connect.status != 0) {
+ /* Connection failed; resume advertising. */
+ bleprph_advertise();
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
+ bleprph_print_conn_desc(&event->disconnect.conn);
+ MODLOG_DFLT(INFO, "\n");
+
+#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
+ phy_conn_changed(CONN_HANDLE_INVALID);
+#endif
+
+ /* Connection terminated; resume advertising. */
+ bleprph_advertise();
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE:
+ /* The central has updated the connection parameters. */
+ MODLOG_DFLT(INFO, "connection updated; status=%d ",
+ event->conn_update.status);
+ rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
+ assert(rc == 0);
+ bleprph_print_conn_desc(&desc);
+ MODLOG_DFLT(INFO, "\n");
+ return 0;
+
+ case BLE_GAP_EVENT_ADV_COMPLETE:
+ MODLOG_DFLT(INFO, "advertise complete; reason=%d",
+ event->adv_complete.reason);
+ bleprph_advertise();
+ 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->connect.conn_handle, &desc);
+ assert(rc == 0);
+ bleprph_print_conn_desc(&desc);
+ MODLOG_DFLT(INFO, "\n");
+ return 0;
+
+ case BLE_GAP_EVENT_SUBSCRIBE:
+ MODLOG_DFLT(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);
+ 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;
+
+#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
+ case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
+ /* XXX: assume symmetric phy for now */
+ phy_update(event->phy_updated.tx_phy);
+ return 0;
+#endif
+ }
+
+ return 0;
+}
+
+static void
+bleprph_on_reset(int reason)
+{
+ MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
+}
+
+static void
+bleprph_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 advertising. */
+ bleprph_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)
+{
+#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0
+ struct image_version ver;
+ static char ver_str[IMGMGR_NMGR_MAX_VER];
+#endif
+ int rc;
+
+ /* Initialize OS */
+ sysinit();
+
+ /* Initialize the NimBLE host configuration. */
+ ble_hs_cfg.reset_cb = bleprph_on_reset;
+ ble_hs_cfg.sync_cb = bleprph_on_sync;
+ ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
+ ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
+
+ rc = gatt_svr_init();
+ assert(rc == 0);
+
+#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0
+ /* Set firmware version in DIS */
+ imgr_my_version(&ver);
+ imgr_ver_str(&ver, ver_str);
+ ble_svc_dis_firmware_revision_set(ver_str);
+#endif
+
+#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
+ phy_init();
+#endif
+
+ 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;
+}
diff --git a/src/libs/mynewt-nimble/apps/bleprph/src/misc.c b/src/libs/mynewt-nimble/apps/bleprph/src/misc.c
new file mode 100644
index 00000000..640b7ff8
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bleprph/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 "bleprph.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(INFO, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
+ }
+}
+
+void
+print_addr(const void *addr)
+{
+ const uint8_t *u8p;
+
+ u8p = addr;
+ MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x",
+ u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
+}
diff --git a/src/libs/mynewt-nimble/apps/bleprph/src/phy.c b/src/libs/mynewt-nimble/apps/bleprph/src/phy.c
new file mode 100644
index 00000000..c6fb2b35
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bleprph/src/phy.c
@@ -0,0 +1,128 @@
+/*
+ * 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 "os/mynewt.h"
+#include "bsp/bsp.h"
+#include "hal/hal_gpio.h"
+#include "host/ble_gap.h"
+#include "bleprph.h"
+
+#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
+
+static const int button_gpio[4] = MYNEWT_VAL(BLEPRPH_LE_PHY_BUTTON_GPIO);
+static const int led_gpio[3] = MYNEWT_VAL(BLEPRPH_LE_PHY_LED_GPIO);
+
+#define PHY_TO_PTR(_mask, _opts) (void *)(((_opts) << 16) | ((_mask)))
+#define PTR_TO_PHY_MASK(_ptr) (uint8_t)(((int)_ptr) & 0x0ff)
+#define PTR_TO_PHY_OPTS(_ptr) (uint8_t)(((int)_ptr) >> 16)
+
+static struct os_event gpio_event;
+
+static uint16_t conn_handle = CONN_HANDLE_INVALID;
+
+static void
+gpio_irq_handler(void *arg)
+{
+ gpio_event.ev_arg = arg;
+ os_eventq_put(os_eventq_dflt_get(), &gpio_event);
+}
+
+static void
+gpio_event_handler(struct os_event *ev)
+{
+ uint8_t phy_mask;
+ uint8_t phy_opts;
+ int sr;
+
+ OS_ENTER_CRITICAL(sr);
+ phy_mask = PTR_TO_PHY_MASK(ev->ev_arg);
+ phy_opts = PTR_TO_PHY_OPTS(ev->ev_arg);
+ OS_EXIT_CRITICAL(sr);
+
+ if (conn_handle != CONN_HANDLE_INVALID) {
+ ble_gap_set_prefered_le_phy(conn_handle, phy_mask, phy_mask, phy_opts);
+ }
+}
+
+static void
+setup_button_gpio(int button, uint8_t phy_mask, uint8_t phy_opts)
+{
+ if (button < 0) {
+ return;
+ }
+
+ hal_gpio_irq_init(button, gpio_irq_handler, PHY_TO_PTR(phy_mask, phy_opts),
+ HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
+ hal_gpio_irq_enable(button);
+}
+
+void
+phy_init(void)
+{
+ gpio_event.ev_cb = gpio_event_handler;
+
+ /*
+ * XXX: we could make this configurable, but for now assume all pins are
+ * valid, buttons gpio pins are pulled-up and LEDs are active-low - this
+ * is valid for nRF52840 PDK.
+ */
+ setup_button_gpio(button_gpio[0], BLE_GAP_LE_PHY_1M_MASK,
+ BLE_GAP_LE_PHY_CODED_ANY);
+ setup_button_gpio(button_gpio[1], BLE_GAP_LE_PHY_2M_MASK,
+ BLE_GAP_LE_PHY_CODED_ANY);
+ setup_button_gpio(button_gpio[2], BLE_GAP_LE_PHY_CODED_MASK,
+ BLE_GAP_LE_PHY_CODED_S2);
+ setup_button_gpio(button_gpio[3], BLE_GAP_LE_PHY_CODED_MASK,
+ BLE_GAP_LE_PHY_CODED_S8);
+
+ hal_gpio_init_out(led_gpio[0], 1);
+ hal_gpio_init_out(led_gpio[1], 1);
+ hal_gpio_init_out(led_gpio[2], 1);
+}
+
+void
+phy_conn_changed(uint16_t handle)
+{
+ uint8_t phy = 0;
+
+ conn_handle = handle;
+
+ if (handle != CONN_HANDLE_INVALID) {
+ /* XXX: assume symmetric phy for now */
+ ble_gap_read_le_phy(handle, &phy, &phy);
+ }
+
+ phy_update(phy);
+}
+
+void
+phy_update(uint8_t phy)
+{
+ if (conn_handle == CONN_HANDLE_INVALID) {
+ hal_gpio_write(led_gpio[0], 1);
+ hal_gpio_write(led_gpio[1], 1);
+ hal_gpio_write(led_gpio[2], 1);
+ } else {
+ hal_gpio_write(led_gpio[0], !(phy == BLE_GAP_LE_PHY_1M));
+ hal_gpio_write(led_gpio[1], !(phy == BLE_GAP_LE_PHY_2M));
+ hal_gpio_write(led_gpio[2], !(phy == BLE_GAP_LE_PHY_CODED));
+ }
+}
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/bleprph/syscfg.yml b/src/libs/mynewt-nimble/apps/bleprph/syscfg.yml
new file mode 100644
index 00000000..c39e6b01
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bleprph/syscfg.yml
@@ -0,0 +1,68 @@
+# 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:
+ BLEPRPH_LE_PHY_SUPPORT:
+ description: >
+ Enable support for changing PHY preference on active connection.
+ PHY preference change is triggered by configured GPIO pins.
+ Current PHY is indicated using LEDs connected to configured
+ GPIO pins.
+ value: 0
+ BLEPRPH_LE_PHY_BUTTON_GPIO:
+ description: >
+ GPIO pins for changing PHY preference on active connection. This
+ is an array of 4 GPIO pin numbers for 1M, 2M, LE Coded S=2 and
+ LE Coded S=8 respectively.
+ value: "(int[]){ BUTTON_1, BUTTON_2, BUTTON_3, BUTTON_4 }"
+ BLEPRPH_LE_PHY_LED_GPIO:
+ description: >
+ GPIO pins for indicating current PHY on active connection. This
+ is an array of 3 GPIO pin numbers for 1M, 2M and LE Coded
+ respectively.
+ value: "(int[]){ LED_1, LED_2, LED_3 }"
+
+syscfg.vals:
+ # Disable central and observer roles.
+ BLE_ROLE_BROADCASTER: 1
+ BLE_ROLE_CENTRAL: 0
+ BLE_ROLE_OBSERVER: 0
+ BLE_ROLE_PERIPHERAL: 1
+
+ # Configure DIS
+ BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM: 1
+
+ # Log reboot messages to a flash circular buffer.
+ REBOOT_LOG_FCB: 1
+ LOG_FCB: 1
+ CONFIG_FCB: 1
+
+ # Enable smp commands.
+ STATS_MGMT: 1
+ LOG_MGMT: 1
+ CONFIG_MGMT: 1
+
+ # OS main/default task
+ OS_MAIN_STACK_SIZE: 512
+
+ # Lots of smaller mbufs are required for smp using typical BLE ATT MTU
+ # values.
+ MSYS_1_BLOCK_COUNT: 22
+ MSYS_1_BLOCK_SIZE: 110
+
+ BLE_SVC_GAP_DEVICE_NAME: '"nimble-bleprph"'
diff --git a/src/libs/mynewt-nimble/apps/blestress/README.md b/src/libs/mynewt-nimble/apps/blestress/README.md
new file mode 100644
index 00000000..8524397a
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/README.md
@@ -0,0 +1,201 @@
+BLE Stress Tests
+
+******************************************************************************
+ QUICK TIPS:
+ You need 2 controllers supported by MyNewt. One will become TX device,
+ the other will become RX device.
+
+ 1. Set role (TX=0, RX=1) for current device in syscfg.yml
+ BLE_STRESS_TEST_ROLE: 1
+
+ The RX has LED2 turned on.
+
+ 2. Set (in syscfg.yml) number of times to repeat each tested action
+ in use case, e.g. do 1000 times connect/disconnect to complete use case.
+ RX_STRESS_REPEAT: 1000
+
+ 3. To perform only specific test, go to rx_stress.c,
+ find definition of rx_stress_main_task_fn(void *arg) and in place of
+ for-loop just paste function rx_stress_start(i), where i is id of test.
+
+******************************************************************************
+
+No | Use case
+-----------------------------------------------------------------------------
+ 1 | Stress Connect -> Connect Cancel - repeat 1000
+ | RX: Nothing
+ | TX: Connect/Connect cancel
+ |
+ 2 | Stress Connect/Disconnect legacy - repeat 1000
+ | RX: Advertise legacy
+ | TX: Connect/Disconnect
+ |
+ 3 | Stress Connect/Disconnect ext adv - repeat 1000
+ | RX: Advertise Ext
+ | TX: Connect/Disconnect
+ |
+ 4 | Stress connection params update (TX) - (1000)
+ | RX: Advertise
+ | TX: Connect/Connect param update
+ |
+ 5 | Stress connection params update (RX) - (1000)
+ | RX: Advertise/Connect param update
+ | TX: Connect
+ |
+ 6 | Stress Scan
+ | RX: Advertise a known data pattern
+ | TX: Scan and check received data with pattern
+ |
+ 7 | Stress PHY Update (TX) - (1000)
+ | RX: Advertise
+ | TX: Connect/Phy update
+ |
+ 8 | Stress PHY update (RX) - (1000)
+ | RX: Advertise/Phy update
+ | TX: Connect
+ |
+ 9 | Stress multi connection
+ | RX: Advertise Ext
+ | TX: Establish and maintain as many instances as possible
+ |
+10 | Stress L2CAP send
+ | RX: Send 64kB of data with L2CAP
+ | TX: Measure bit rate and max received data MTU
+ |
+11 | Stress Advertise/Connect/Continue Adv/Disconnect
+ | RX: Advertise Ext/Continue same advertise after connect
+ | TX: Connect
+ |
+12 | Stress GATT indicating
+ | RX: Indicate
+ | TX: Receive indication. Measure average time of indicating.
+ |
+13 | Stress GATT notification
+ | RX: Notify. Measure average time of notifying.
+ | TX: Count the number of received notification.
+ |
+14 | Stress GATT Subscribe/Notify/Unsubscribe
+ | RX: Notify on subscribe
+ | TX: Measure the average time from sending a subscription request
+ | to receiving a notification.
+ |
+15 | Stress Connect/Send/Disconnect
+ | RX: Advertise/Send via ATT/Disconnect
+ | TX: Receive notification. Measure time of notifying.
+
+******************************************************************************
+ Concept:
+ The RX device advertises data containing a UUID128 of test use case that
+ should be performed. The TX device scan for known UUIDs, when it finds,
+ adapts to the advertised use case and runs a test.
+
+ Stress Task vs Semaphore:
+ The rx_stress_start_auto function starts main stress test task that runs
+ all stress tests one by one. The tests are based on event handling, so to
+ synchronize main task with events, a semaphore is used. On use case start
+ there is 0 tokens for semaphore. After main task starts one of use cases,
+ it comes across a semaphore and has to wait. When use case is completed,
+ 1 token is released, so main task can use it to pass through semaphore.
+ The tx_stress_start_auto function works analogically.
+
+
+ Newt target set example:
+ rymek@rymek:~/projects/bletiny_proj$ newt target show bletest_tx
+ targets/bletest_tx
+ app=@apache-mynewt-nimble/apps/blestress
+ bsp=@apache-mynewt-core/hw/bsp/nordic_pca10056
+ build_profile=debug
+ syscfg=BLE_STRESS_TEST_ROLE=0
+ rymek@rymek:~/projects/bletiny_proj$ newt target show bletest_rx
+ targets/bletest_rx
+ app=@apache-mynewt-nimble/apps/blestress
+ bsp=@apache-mynewt-core/hw/bsp/nordic_pca10056
+ build_profile=debug
+ syscfg=BLE_STRESS_TEST_ROLE=1
+
+
+ Example of expected logs on TX side(LOG_LEVEL > 1):
+ Start test num 1
+ >>>>>>>>>>>>>>>>>>>> Stress test 1 completed
+ Start scan for test
+ Start test num 2
+ >>>>>>>>>>>>>>>>>>>> Stress test 2 completed
+ Start scan for test
+ Start test num 3
+ >>>>>>>>>>>>>>>>>>>> Stress test 3 completed
+ Start scan for test
+ Start test num 4
+ >>>>>>>>>>>>>>>>>>>> Stress test 4 completed
+ Start scan for test
+ Start test num 5
+ >>>>>>>>>>>>>>>>>>>> Stress test 5 completed
+ Start scan for test
+ Start test num 6
+ >>>>>>>>>>>>>>>>>>>> Stress test 6 completed
+ Start scan for test
+ Start test num 7
+ >>>>>>>>>>>>>>>>>>>> Stress test 7 completed
+ Start scan for test
+ Start test num 8
+ >>>>>>>>>>>>>>>>>>>> Stress test 8 completed
+ All tests completed
+ Tests results:
+ Use case 1 - Stress Connect -> Connect Cancel:
+ Con attempts = 20
+ Con success = 0
+ Use case 2 - Stress Connect/Disconnect legacy:
+ Con attempts = 20
+ Con success = 20
+ Use case 3 - Stress Connect/Disconnect ext adv:
+ Con attempts = 20
+ Con success = 20
+ Use case 4 - Stress connection params update (TX):
+ Params updates = 20
+ Use case 5 - Stress connection params update (RX):
+ Params updates = 20
+ Use case 6 - Stress Scan:
+ Received first packets = 20
+ Received all packets = 20
+ Use case 7 - Stress PHY Update (TX):
+ PHY updates = 20
+ Use case 8 - Stress Connect -> Connect Cancel:
+ PHY updates = 20
+
+
+ Example of expected logs on RX side(LOG_LEVEL > 1):
+ Start test num 2
+ >>>>>>>>>>>>>>>>>>>> Stress test 2 completed
+ Start test num 3
+ >>>>>>>>>>>>>>>>>>>> Stress test 3 completed
+ Start test num 4
+ >>>>>>>>>>>>>>>>>>>> Stress test 4 completed
+ Start test num 5
+ >>>>>>>>>>>>>>>>>>>> Stress test 5 completed
+ Start test num 6
+ Received signal to switch test
+ Start test num 7
+ >>>>>>>>>>>>>>>>>>>> Stress test 7 completed
+ Start test num 8
+ >>>>>>>>>>>>>>>>>>>> Stress test 8 completed
+ All tests completed
+ Tests results:
+ Use case 1 - Stress Connect -> Connect Cancel:
+ Con attempts = 0
+ Con success = 0
+ Use case 2 - Stress Connect/Disconnect legacy:
+ Con attempts = 20
+ Con success = 20
+ Use case 3 - Stress Connect/Disconnect ext adv:
+ Con attempts = 20
+ Con success = 20
+ Use case 4 - Stress connection params update (TX):
+ Params updates = 20
+ Use case 5 - Stress connection params update (RX):
+ Params updates = 20
+ Use case 6 - Stress Scan:
+ Received first packets = 0
+ Received all packets = 0
+ Use case 7 - Stress PHY Update (TX):
+ PHY updates = 20
+ Use case 8 - Stress Connect -> Connect Cancel:
+ PHY updates = 20
diff --git a/src/libs/mynewt-nimble/apps/blestress/pkg.yml b/src/libs/mynewt-nimble/apps/blestress/pkg.yml
new file mode 100644
index 00000000..da23ecb0
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/pkg.yml
@@ -0,0 +1,39 @@
+#
+# 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/blestress"
+pkg.type: app
+pkg.description: "Stress tests sample application."
+pkg.keywords:
+
+pkg.deps:
+ - "@mcuboot/boot/bootutil"
+ - "@apache-mynewt-core/sys/log/modlog"
+ - "@apache-mynewt-core/kernel/os"
+ - "@apache-mynewt-core/sys/console/full"
+ - "@apache-mynewt-core/sys/log/full"
+ - "@apache-mynewt-core/sys/stats/full"
+ - "@apache-mynewt-core/sys/id"
+ - "@apache-mynewt-nimble/nimble/controller"
+ - "@apache-mynewt-nimble/nimble/host"
+ - "@apache-mynewt-nimble/nimble/host/util"
+ - "@apache-mynewt-nimble/nimble/host/services/gap"
+ - "@apache-mynewt-nimble/nimble/host/services/gatt"
+ - "@apache-mynewt-nimble/nimble/host/store/ram"
+ - "@apache-mynewt-nimble/nimble/transport/ram"
diff --git a/src/libs/mynewt-nimble/apps/blestress/src/main.c b/src/libs/mynewt-nimble/apps/blestress/src/main.c
new file mode 100644
index 00000000..ec28ed8a
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/src/main.c
@@ -0,0 +1,99 @@
+/*
+ * 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 "os/mynewt.h"
+#include "config/config.h"
+#include "bsp.h"
+#include "hal/hal_gpio.h"
+
+/* BLE */
+#include "nimble/ble.h"
+#include "host/ble_hs.h"
+#include "host/util/util.h"
+#include "services/gap/ble_svc_gap.h"
+
+/* Application-specified header. */
+#include "rx_stress.h"
+#include "tx_stress.h"
+#include "stress_gatt.h"
+
+static void
+stress_test_on_reset(int reason)
+{
+ MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
+}
+
+static void
+stress_test_on_sync(void)
+{
+ int rc;
+
+ /* Make sure we have proper identity address set (public preferred) */
+ rc = ble_hs_util_ensure_addr(1);
+ assert(rc == 0);
+
+#if MYNEWT_VAL(BLE_STRESS_TEST_ROLE)
+ rx_stress_start_auto();
+#else
+ tx_stress_start_auto();
+#endif
+}
+
+/**
+ * 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;
+
+ sysinit();
+
+ ble_hs_cfg.reset_cb = stress_test_on_reset;
+ ble_hs_cfg.sync_cb = stress_test_on_sync;
+ ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
+
+ /* Please do not change name. Otherwise some tests could fail. */
+ rc = ble_svc_gap_device_name_set("STRESS");
+ assert(rc == 0);
+
+ conf_load();
+
+ rc = gatt_svr_init();
+ assert(rc == 0);
+
+#if MYNEWT_VAL(BLE_STRESS_TEST_ROLE)
+ /* RX device */
+ ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
+ hal_gpio_init_out(LED_2, 1);
+ hal_gpio_toggle(LED_2);
+#endif
+
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/blestress/src/misc.c b/src/libs/mynewt-nimble/apps/blestress/src/misc.c
new file mode 100644
index 00000000..bd71a871
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/src/misc.c
@@ -0,0 +1,224 @@
+/*
+ * 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 "misc.h"
+
+void
+rand_bytes(uint8_t *data, int len) {
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ data[i] = (uint8_t) rand() % UINT8_MAX;
+ }
+}
+
+/**
+ * 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_addr(const void *addr)
+{
+ const uint8_t *u8p;
+
+ u8p = addr;
+ MODLOG_DFLT(DEBUG, "%02x:%02x:%02x:%02x:%02x:%02x",
+ u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
+}
+
+void
+print_mbuf(const struct os_mbuf *om)
+{
+ while (om != NULL) {
+ print_bytes(om->om_data, om->om_len);
+ om = SLIST_NEXT(om, om_next);
+
+ if(om == NULL) {
+ return;
+ }
+
+ /* Separate buf fields with colon to maintain continuity */
+ MODLOG_DFLT(DEBUG, ":");
+ }
+}
+
+char *
+addr_str(const void *addr)
+{
+ /* 6 bytes of MAC address * 2 signs for each byte in string + 5 colons to
+ * separate bytes + 1 byte for null-character appended by sprintf
+ */
+ 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/blestress/src/misc.h b/src/libs/mynewt-nimble/apps/blestress/src/misc.h
new file mode 100644
index 00000000..39e5e737
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/src/misc.h
@@ -0,0 +1,54 @@
+/*
+ * 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 BLE_TGT_MISC_H
+#define BLE_TGT_MISC_H
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include "host/ble_hs.h"
+#include "host/ble_uuid.h"
+#include "host/ble_hs_adv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void rand_bytes(uint8_t *data, int len);
+
+void print_bytes(const uint8_t *bytes, int len);
+
+void print_addr(const void *addr);
+
+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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //BLE_TGT_MISC_H
diff --git a/src/libs/mynewt-nimble/apps/blestress/src/rx_stress.c b/src/libs/mynewt-nimble/apps/blestress/src/rx_stress.c
new file mode 100644
index 00000000..a4253ce6
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/src/rx_stress.c
@@ -0,0 +1,1471 @@
+/*
+ * 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 <host/ble_gap.h>
+#include "rx_stress.h"
+
+/* UUID128 of stress test use cases*/
+static uint8_t rx_stress_uuid128[STRESS_UUIDS_NUM][16];
+
+static struct com_stress_test_ctx rx_stress_ctxD = {
+ .conn_handle = 0xffff,
+ .cur_test_id = 0,
+ .s6_rcv_adv_first = 0,
+ .s6_rcv_adv_suc = 0,
+};
+
+static struct com_stress_test_ctx *rx_stress_ctx = &rx_stress_ctxD;
+
+#define EXTENDED_ADVERT 0
+#define LEGACY_ADVERT 1
+/* Advertising instances ids */
+#define SWITCHER_INSTANCE 0
+#define TEST_INSTANCE 1
+/* Main test task priority. Set a high value so that the task does not
+ * interfere with event handling */
+#define RX_STRESS_MAIN_TASK_PRIO 0xf0
+
+/* Advertising settings */
+struct rx_stress_adv_set {
+ uint8_t instance;
+ uint8_t *instance_uuid128;
+ uint8_t legacy_pdu;
+ ble_gap_event_fn *cb;
+ const uint8_t *pattern_data;
+ int pattern_len;
+};
+
+/* Define task stack and task object */
+#define RX_STRESS_MAIN_TASK_STACK_SIZE (2000)
+static struct os_task rx_stress_main_task;
+static os_stack_t rx_stress_main_task_stack[RX_STRESS_MAIN_TASK_STACK_SIZE];
+static struct os_sem rx_stress_main_sem;
+
+static void
+rx_stress_on_test_finish(int test_num)
+{
+ console_printf("\033[0;32m\nStress test %d completed\033[0m\n", test_num);
+ os_sem_release(&rx_stress_main_sem);
+}
+
+static int
+rx_stress_adv_start(uint8_t instance)
+{
+ int rc;
+
+ /* Resume advertising earlier configured instance */
+ rc = ble_gap_ext_adv_start(instance, 0, 0);
+ assert (rc == 0 || rc == 2);
+ MODLOG_DFLT(INFO, "Instance %d started; rc: %d\n", instance, rc);
+ return rc;
+}
+
+static int
+rx_stress_adv_start_with_rand_addr(uint8_t instance)
+{
+ int rc;
+ ble_addr_t addr;
+
+ ble_gap_ext_adv_stop(instance);
+
+ rc = ble_hs_id_gen_rnd(1, &addr);
+ assert (rc == 0);
+
+ /* Set random address for advertising instance */
+ rc = ble_gap_ext_adv_set_addr(instance, &addr);
+ assert (rc == 0);
+
+ return rx_stress_adv_start(instance);
+}
+
+static void
+rx_stress_simple_adv(struct rx_stress_adv_set *adv_set)
+{
+ uint8_t own_addr_type;
+ struct ble_gap_ext_adv_params params;
+ struct ble_hs_adv_fields fields;
+ struct os_mbuf *adv_data;
+ ble_addr_t addr;
+ const char *name;
+ int rc;
+ int pattern_len;
+
+ /* Determine own address type */
+ rc = ble_hs_id_infer_auto(0, &own_addr_type);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "\033[0;31mError determining own address type; "
+ "rc=%d\033[0m\n", rc);
+ return;
+ }
+
+ /* Use defaults for non-set fields */
+ memset(&fields, 0, sizeof fields);
+ /* General Discoverable and BrEdrNotSupported */
+ fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
+ /* Set device name as instance name */
+ name = ble_svc_gap_device_name();
+ fields.name = (uint8_t *) name;
+ fields.name_len = strlen(name);
+ fields.name_is_complete = 1;
+ /* Set UUID 128 data service */
+ fields.svc_data_uuid128 = adv_set->instance_uuid128;
+ fields.svc_data_uuid128_len = 16;
+
+ /* Use defaults for non-set params */
+ memset(&params, 0, sizeof(params));
+ /* Set adv mode */
+ if (adv_set->legacy_pdu == 1) {
+ params.connectable = 1;
+ params.scannable = 1;
+ } else if (adv_set->pattern_len < 255) {
+ params.connectable = 1;
+ }
+
+ params.own_addr_type = own_addr_type;
+ params.primary_phy = BLE_HCI_LE_PHY_1M;
+ /* If legacy, this param will be lowered by API */
+ params.secondary_phy = BLE_HCI_LE_PHY_2M;
+ params.sid = adv_set->instance;
+ params.legacy_pdu = adv_set->legacy_pdu;
+
+ ble_gap_set_prefered_default_le_phy(TX_PHY_MASK, RX_PHY_MASK);
+
+ rc = ble_gap_ext_adv_remove(adv_set->instance);
+ assert(rc == 0 || rc == BLE_HS_EALREADY);
+
+ /* Configure instance with the params set */
+ rc = ble_gap_ext_adv_configure(adv_set->instance, &params,
+ NULL, adv_set->cb, NULL);
+ assert (rc == 0);
+
+ if (own_addr_type == 0) {
+ memcpy(addr.val, MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR), 6);
+ } else {
+ rc = ble_hs_id_gen_rnd(1, &addr);
+ assert (rc == 0);
+
+ /* Set random address for advertising instance */
+ rc = ble_gap_ext_adv_set_addr(adv_set->instance, &addr);
+ assert (rc == 0);
+ }
+
+ /* Get mbuf for adv data */
+ adv_data = os_msys_get_pkthdr(16, 0);
+ assert(adv_data != NULL);
+
+ /* Fill mbuf with adv fields - structured data */
+ rc = ble_hs_adv_set_fields_mbuf(&fields, adv_data);
+
+ if (rc) {
+ os_mbuf_free_chain(adv_data);
+ assert(0);
+ }
+
+ pattern_len = min(adv_set->pattern_len,
+ MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE) - adv_data->om_len);
+
+ /* Append to mbuf pattern data - raw data */
+ rc = os_mbuf_append(adv_data, adv_set->pattern_data, pattern_len);
+
+ if (rc) {
+ os_mbuf_free_chain(adv_data);
+ assert(0);
+ }
+
+ /* Include mbuf data in advertisement */
+ rc = ble_gap_ext_adv_set_data(adv_set->instance, adv_data);
+ assert (rc == 0);
+
+ /* Start advertising */
+ rc = ble_gap_ext_adv_start(adv_set->instance, 0, 0);
+ assert (rc == 0);
+
+ MODLOG_DFLT(INFO, "instance %u started\n", adv_set->instance);
+}
+
+static int
+rx_stress_0_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[0].num);
+
+ /* Stop test advert */
+ ble_gap_ext_adv_stop(TEST_INSTANCE);
+ ble_gap_terminate(event->connect.conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ } else {
+ /* Connection failed; resume advertising */
+ MODLOG_DFLT(INFO, "Connection failed; status=%d ",
+ event->connect.status);
+ }
+ return 0;
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ console_printf("\033[0;32mReceived signal to switch test\033[0m\n");
+ /* Add token to semaphore. Main task will start next test. */
+ os_sem_release(&rx_stress_main_sem);
+
+ return 0;
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+rx_stress_2_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ ++rx_stress_ctx->con_stat[2].attempts_num;
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[2].num);
+ } else {
+ /* Connection failed; resume advertising. */
+ MODLOG_DFLT(INFO, "Connection failed; status=%d ",
+ event->connect.status);
+
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ console_printf("\033[0;32m>\033[0m");
+ if (rx_stress_ctx->con_stat[2].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ rx_stress_on_test_finish(2);
+ } else {
+ /* Connection terminated; resume advertising. */
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+rx_stress_3_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ ++rx_stress_ctx->con_stat[3].attempts_num;
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[3].num);
+ } else {
+ /* Connection failed; resume advertising. */
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ console_printf("\033[0;32m>\033[0m");
+ if (rx_stress_ctx->con_stat[3].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ rx_stress_on_test_finish(3);
+ } else {
+ /* Connection terminated; resume advertising. */
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+rx_stress_4_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:
+ ++rx_stress_ctx->con_stat[4].attempts_num;
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[4].num);
+
+ /* Remember connection handler */
+ rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+ assert(rc == 0);
+ rx_stress_ctx->conn_handle = event->connect.conn_handle;
+ } else {
+ /* Connection failed; resume advertising. */
+ MODLOG_DFLT(INFO, "Connection failed; status=%d ",
+ event->connect.status);
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
+ if (rx_stress_ctx->con_stat[4].prms_upd_num >=
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ rx_stress_on_test_finish(4);
+ } else {
+ /* Connection terminated; resume advertising. */
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE:
+ if (event->conn_update.status != 0) {
+ MODLOG_DFLT(INFO, "Connection update failed\n");
+ } else {
+ MODLOG_DFLT(INFO, "Connection updated; num=%d\n",
+ ++rx_stress_ctx->con_stat[4].prms_upd_num);
+ console_printf("\033[0;32m>\033[0m");
+ }
+
+ if (rx_stress_ctx->con_stat[4].prms_upd_num >=
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ /* Test completed. */
+ ble_gap_terminate(rx_stress_ctx->conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ }
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+rx_stress_5_con_update(void)
+{
+ int rc;
+
+ /* With every next update at least one param must change. Otherwise no
+ * event occurs and test will not be continued */
+ struct ble_gap_upd_params params = {
+ .itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN,
+ .itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX,
+ .latency = BLE_GAP_INITIAL_CONN_LATENCY,
+ /* So let's change e.g. timeout value. Put ...% 2 ? 1 : 2 to make sure
+ * that value won't grow significantly and will be different with every
+ * iteration. */
+ .supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT +
+ (rx_stress_ctx->con_stat[5].prms_upd_num % 2 ?
+ 1 : 2),
+ .min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN,
+ .max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN,
+ };
+
+ rc = ble_gap_update_params(rx_stress_ctx->conn_handle, &params);
+
+ if (rc == BLE_HS_ENOTCONN) {
+ MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n");
+ assert(0);
+ }
+
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "\033[0;31mError during connection update; "
+ "rc=%d\033[0m\n", rc);
+ assert(0);
+ }
+
+ return rc;
+}
+
+static int
+rx_stress_5_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:
+ ++rx_stress_ctx->con_stat[5].attempts_num;
+
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[5].num);
+
+ /* Remember connection handler */
+ rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+ assert(rc == 0);
+ rx_stress_ctx->conn_handle = event->connect.conn_handle;
+
+ /* Update connection. */
+ rc = rx_stress_5_con_update();
+ assert(rc == 0);
+ } else {
+ /* Connection failed; resume advertising. */
+ MODLOG_DFLT(INFO, "Connection failed; status=%d ",
+ event->connect.status);
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
+ if (rx_stress_ctx->con_stat[5].prms_upd_num >=
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ rx_stress_on_test_finish(5);
+ } else {
+ /* Connection terminated; resume advertising. */
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE:
+ if (event->conn_update.status != 0) {
+ MODLOG_DFLT(INFO, "Connection update failed\n");
+ } else {
+ MODLOG_DFLT(INFO, "Connection updated; num=%d\n",
+ ++rx_stress_ctx->con_stat[5].prms_upd_num);
+ console_printf("\033[0;32m>\033[0m");
+ }
+
+ if (rx_stress_ctx->con_stat[5].prms_upd_num >=
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ /* Test completed. */
+ ble_gap_terminate(rx_stress_ctx->conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ } else {
+ /* Update connection. */
+ rc = rx_stress_5_con_update();
+ assert(rc == 0);
+ }
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+rx_stress_6_gap_event(struct ble_gap_event *event, void *arg)
+{
+ MODLOG_DFLT(INFO, "Event occurs=%d\n", event->type);
+ return 0;
+}
+
+static int
+rx_stress_7_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:
+ ++rx_stress_ctx->con_stat[7].attempts_num;
+
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[7].num);
+
+ /* Remember connection handler */
+ rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+ assert(rc == 0);
+ rx_stress_ctx->conn_handle = event->connect.conn_handle;
+ } else {
+ /* Connection failed; resume advertising. */
+ MODLOG_DFLT(INFO, "Connection failed; status=%d ",
+ event->connect.status);
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
+ if (rx_stress_ctx->con_stat[7].phy_upd_num >=
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ rx_stress_on_test_finish(7);
+ } else {
+ /* Connection terminated; resume advertising. */
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
+ if (event->phy_updated.status != 0) {
+ MODLOG_DFLT(INFO, "PHY update failed\n");
+ } else {
+ MODLOG_DFLT(INFO, "PHY updated; num=%d\n",
+ ++rx_stress_ctx->con_stat[7].phy_upd_num);
+ console_printf("\033[0;32m>\033[0m");
+ }
+
+ if (rx_stress_ctx->con_stat[7].phy_upd_num >=
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ /* Test completed. */
+ ble_gap_terminate(rx_stress_ctx->conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ }
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+rx_stress_8_con_update(void)
+{
+ int rc;
+ uint8_t tx_phys_mask;
+ uint8_t rx_phys_mask;
+
+ ble_gap_read_le_phy(rx_stress_ctx->conn_handle, &tx_phys_mask,
+ &rx_phys_mask);
+
+ /* With every next update at least one param must change */
+ switch (rx_phys_mask) {
+ case BLE_GAP_LE_PHY_1M_MASK:
+ rx_phys_mask = BLE_GAP_LE_PHY_2M_MASK;
+ break;
+ case BLE_GAP_LE_PHY_2M_MASK:
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
+ rx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK;
+ break;
+ case BLE_GAP_LE_PHY_CODED_MASK:
+#endif
+ rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK;
+ break;
+ default:
+ rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK;
+ break;
+ }
+
+ switch (tx_phys_mask) {
+ case BLE_GAP_LE_PHY_1M_MASK:
+ tx_phys_mask = BLE_GAP_LE_PHY_2M_MASK;
+ break;
+ case BLE_GAP_LE_PHY_2M_MASK:
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
+ tx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK;
+ break;
+ case BLE_GAP_LE_PHY_CODED_MASK:
+#endif
+ tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK;
+ break;
+ default:
+ tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK;
+ break;
+ }
+
+ rc = ble_gap_set_prefered_le_phy(rx_stress_ctx->conn_handle,
+ tx_phys_mask, rx_phys_mask, 0);
+
+ if (rc == BLE_HS_ENOTCONN) {
+ MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n");
+ return rc;
+ }
+
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "\033[0;31mError during PHY update; "
+ "rc=%d\033[0m\n", rc);
+ }
+
+ return rc;
+}
+
+static int
+rx_stress_8_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:
+ ++rx_stress_ctx->con_stat[8].attempts_num;
+
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[8].num);
+
+ /* Remember connection handler */
+ rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+ assert(rc == 0);
+ rx_stress_ctx->conn_handle = event->connect.conn_handle;
+
+ /* Update connection. */
+ rc = rx_stress_8_con_update();
+ assert(rc == 0);
+ } else {
+ /* Connection failed; resume advertising. */
+ MODLOG_DFLT(INFO, "Connection failed; status=%d ",
+ event->connect.status);
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
+ if (rx_stress_ctx->con_stat[8].phy_upd_num >=
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ rx_stress_on_test_finish(8);
+ } else {
+ /* Connection terminated; resume advertising. */
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
+ if (event->phy_updated.status != 0) {
+ MODLOG_DFLT(INFO, "PHY update failed\n");
+ } else {
+ MODLOG_DFLT(INFO, "PHY updated; num=%d; rx:%d, tx:%d\n",
+ ++rx_stress_ctx->con_stat[8].phy_upd_num,
+ event->phy_updated.rx_phy, event->phy_updated.tx_phy);
+ console_printf("\033[0;32m>\033[0m");
+ }
+
+ if (rx_stress_ctx->con_stat[8].phy_upd_num >=
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ /* Test completed. */
+ ble_gap_terminate(rx_stress_ctx->conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ } else {
+ /* Update connection. */
+ rc = rx_stress_8_con_update();
+ assert(rc == 0);
+ }
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+rx_stress_9_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ ++rx_stress_ctx->con_stat[9].attempts_num;
+
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[9].num);
+ console_printf("\033[0;32m>\033[0m");
+
+ /* Remember max number of established connections */
+ if (rx_stress_ctx->con_stat[9].num >
+ rx_stress_ctx->con_stat[9].max_num) {
+ rx_stress_ctx->con_stat[9].max_num = rx_stress_ctx->con_stat[9].num;
+ }
+ } else {
+ /* Connection failed; resume advertising. */
+ MODLOG_DFLT(INFO, "Connection failed; status=%d ",
+ event->connect.status);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
+ console_printf("\033[0;31mX\033[0m");
+ MODLOG_DFLT(INFO, "Connections num: %d\n",
+ --rx_stress_ctx->con_stat[9].num);
+
+ if (rx_stress_ctx->con_stat[9].num != 0 &&
+ rx_stress_ctx->con_stat[9].num <
+ MYNEWT_VAL(BLE_MAX_CONNECTIONS) + 1) {
+ rx_stress_adv_start_with_rand_addr(TEST_INSTANCE);
+ } else {
+ /* When TX device has terminated all connections, stop advertising. */
+ ble_gap_ext_adv_stop(TEST_INSTANCE);
+ rx_stress_on_test_finish(9);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_ADV_COMPLETE:
+ /* Stop test when TX device has terminated all connections or
+ * number of connections has reached the max possible value. */
+ if (rx_stress_ctx->con_stat[9].num != 0 &&
+ rx_stress_ctx->con_stat[9].num <
+ MYNEWT_VAL(BLE_MAX_CONNECTIONS) + 1) {
+ rx_stress_adv_start_with_rand_addr(TEST_INSTANCE);
+ }
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ }
+ return 0;
+}
+
+static void
+tx_stress_10_l2cap_update_event(uint16_t conn_handle, int status, void *arg)
+{
+ if (status == 0) {
+ MODLOG_DFLT(INFO, "L2CAP params updated\n");
+ } else {
+ MODLOG_DFLT(INFO, "L2CAP params update failed; rc=%d\n", status);
+ assert(0);
+ }
+}
+
+static int
+rx_stress_10_l2cap_event(struct ble_l2cap_event *event, void *arg)
+{
+ int rc;
+ struct os_mbuf *data_buf;
+ static int data_len = 1000;
+ static int send_cnt = 0;
+ static bool stalled = false;
+ struct ble_l2cap_chan_info chan_info;
+
+ switch (event->type) {
+ case BLE_L2CAP_EVENT_COC_CONNECTED:
+ if (event->connect.status) {
+ MODLOG_DFLT(INFO, "LE COC error: %d\n", event->connect.status);
+ return 0;
+ }
+
+ ble_l2cap_get_chan_info(event->connect.chan, &chan_info);
+
+ MODLOG_DFLT(INFO,
+ "LE COC connected, conn: %d, chan: 0x%08lx, scid: 0x%04x, "
+ "dcid: 0x%04x, our_mtu: 0x%04x, peer_mtu: 0x%04x\n",
+ event->connect.conn_handle,
+ (uint32_t) event->connect.chan,
+ chan_info.scid,
+ chan_info.dcid,
+ chan_info.our_l2cap_mtu,
+ chan_info.peer_l2cap_mtu);
+
+ struct ble_l2cap_sig_update_params params = {
+ .itvl_min = 0x0006,//BLE_GAP_INITIAL_CONN_ITVL_MIN
+ .itvl_max = 0x0006,//BLE_GAP_INITIAL_CONN_ITVL_MIN
+ .slave_latency = 0x0000,
+ .timeout_multiplier = 0x0100,
+ };
+
+ rc = ble_l2cap_sig_update(event->connect.conn_handle, &params,
+ &tx_stress_10_l2cap_update_event, NULL);
+ assert(rc == 0);
+ return 0;
+
+ case BLE_L2CAP_EVENT_COC_DISCONNECTED:
+ MODLOG_DFLT(INFO, "LE CoC disconnected, chan: 0x%08lx\n",
+ (uint32_t) event->disconnect.chan);
+ return 0;
+
+ case BLE_L2CAP_EVENT_COC_ACCEPT:
+ stress_l2cap_coc_accept(event->accept.peer_sdu_size,
+ event->accept.chan);
+ return 0;
+
+ case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
+ stress_l2cap_coc_recv(event->receive.chan, event->receive.sdu_rx);
+
+ MODLOG_DFLT(INFO, "L2CAP server received data; num=%d\n",
+ ++rx_stress_ctx->rcv_num);
+ rx_stress_ctx->chan = event->receive.chan;
+
+ /* In this use case, receiving any data by RX device L2CAP server means
+ * request from TX device to send data. */
+
+ /* Do not send if stalled on the last sending. */
+ if (stalled) {
+ return 0;
+ }
+ break;
+
+ case BLE_L2CAP_EVENT_COC_TX_UNSTALLED:
+ MODLOG_DFLT(INFO, "L2CAP unstalled event\n");
+
+ stalled = false;
+
+ /* Send if was stalled on the last request to send. */
+ if (rx_stress_ctx->rcv_num > send_cnt) {
+ break;
+ }
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other L2CAP event occurs: %d\n", event->type);
+ return 0;
+ }
+
+ /* Send pattern data */
+
+ /* Get mbuf for adv data */
+ data_buf = os_msys_get_pkthdr(data_len, 0);
+
+ MODLOG_DFLT(INFO, "Data buf %s\n", data_buf ? "OK" : "NOK");
+ assert(data_buf != NULL);
+
+ /* The first 2 bytes of data is the size of appended pattern data. */
+ rc = os_mbuf_append(data_buf, (uint8_t[]) {data_len >> 8, data_len},
+ 2);
+ if (rc) {
+ os_mbuf_free_chain(data_buf);
+ assert(0);
+ }
+
+ /* Fill mbuf with the pattern */
+ stress_fill_mbuf_with_pattern(data_buf, data_len);
+
+ /* Send data */
+ rc = ble_l2cap_send(rx_stress_ctx->chan, data_buf);
+ MODLOG_DFLT(INFO, "Return code=%d\n", rc);
+ if (rc) {
+ MODLOG_DFLT(INFO, "L2CAP stalled - waiting\n");
+ stalled = true;
+ }
+
+ MODLOG_DFLT(INFO, " %d, %d\n", ++send_cnt, data_len);
+ data_len += 500;
+
+ return 0;
+}
+
+static int
+rx_stress_10_gap_event(struct ble_gap_event *event, void *arg)
+{
+ struct ble_gap_conn_desc out_desc;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ ++rx_stress_ctx->con_stat[10].attempts_num;
+
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[10].num);
+
+ ble_gap_conn_find(event->connect.conn_handle, &out_desc);
+ MODLOG_DFLT(INFO, "Address %s",
+ addr_str(out_desc.peer_id_addr.val));
+ } else {
+ /* Connection failed; resume advertising. */
+ MODLOG_DFLT(INFO, "Connection failed; status=%d ",
+ event->connect.status);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
+ rx_stress_ctx->completed[10] = true;
+ rx_stress_on_test_finish(10);
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+rx_stress_11_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ ++rx_stress_ctx->con_stat[11].attempts_num;
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[11].num);
+ } else {
+ MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
+ "status=%d\033[0m\n", event->connect.status);
+ }
+
+ ble_gap_terminate(event->connect.conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ console_printf("\033[0;32m>\033[0m");
+ if (rx_stress_ctx->con_stat[11].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ rx_stress_on_test_finish(11);
+ } else {
+ /* Connection terminated; resume advertising. */
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+rx_stress_12_gap_event(struct ble_gap_event *event, void *arg)
+{
+ int om_len = 10000;
+ struct os_mbuf *om;
+ int64_t us = 0;
+ 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) {
+ MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[12].num);
+ rx_stress_ctx->conn_handle = event->connect.conn_handle;
+
+ break;
+ } else {
+ /* Connection failed; resume advertising */
+ MODLOG_DFLT(INFO, "Connection failed; status=%d ",
+ event->connect.status);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+
+ rx_stress_ctx->s12_notif_time = rx_stress_ctx->time_sum /
+ rx_stress_ctx->send_num;
+
+ MODLOG_DFLT(INFO, "Average time: %d us\n",
+ rx_stress_ctx->s12_notif_time);
+
+ rx_stress_on_test_finish(12);
+ return 0;
+
+ case BLE_GAP_EVENT_NOTIFY_TX:
+ rx_stress_ctx->end_us = os_get_uptime_usec();
+ MODLOG_DFLT(INFO, "Notify TX event\n");
+
+ if (!event->notify_tx.status) {
+ /* Send next only after previous indication is done */
+ return 0;
+ }
+ assert(event->notify_tx.status == BLE_HS_EDONE);
+
+ if (rx_stress_ctx->send_num++ >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ ble_gap_terminate(event->notify_tx.conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ return 0;
+ }
+
+ /* Time of data sending */
+ us = rx_stress_ctx->end_us - rx_stress_ctx->begin_us;
+ console_printf("Indication time: %lld\n", us);
+ rx_stress_ctx->time_sum += us;
+ console_printf("\033[0;32m>\033[0m");
+ break;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+
+ /* Indicate data pattern */
+ rx_stress_ctx->begin_us = os_get_uptime_usec();
+ om = os_msys_get_pkthdr(om_len, 0);
+ stress_fill_mbuf_with_pattern(om, om_len);
+ rc = ble_gattc_indicate_custom(rx_stress_ctx->conn_handle, hrs_hrm_handle,
+ om);
+ assert(rc == 0);
+ return 0;
+}
+
+static int
+rx_stress_13_gap_event(struct ble_gap_event *event, void *arg)
+{
+ int rc;
+ struct os_mbuf *om = NULL;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[13].num);
+ rx_stress_ctx->conn_handle = event->connect.conn_handle;
+
+ rx_stress_ctx->begin_us = os_get_uptime_usec();
+ break;
+ } else {
+ /* Connection failed; resume advertising */
+ MODLOG_DFLT(INFO, "Connection failed; status=%d ",
+ event->connect.status);
+ assert(0);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+
+ rx_stress_ctx->time_sum = rx_stress_ctx->end_us -
+ rx_stress_ctx->begin_us;
+
+ rx_stress_ctx->s13_notif_time = rx_stress_ctx->time_sum /
+ rx_stress_ctx->send_num;
+
+ MODLOG_DFLT(INFO, "Average time: %lld us\n",
+ rx_stress_ctx->s13_notif_time);
+ rx_stress_on_test_finish(13);
+ return 0;
+
+ case BLE_GAP_EVENT_NOTIFY_TX:
+ MODLOG_DFLT(INFO, "Notify TX event; num=%d\n",
+ ++rx_stress_ctx->send_num);
+ assert(event->notify_tx.status == 0);
+
+ if (rx_stress_ctx->send_num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ rx_stress_ctx->end_us = os_get_uptime_usec();
+ ble_gap_terminate(event->connect.conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ return 0;
+ }
+ break;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+
+ om = ble_hs_mbuf_from_flat(test_6_pattern, 10);
+ rc = ble_gattc_notify_custom(rx_stress_ctx->conn_handle,
+ hrs_hrm_handle, om);
+ assert(rc == 0);
+ return 0;
+}
+
+static int
+rx_stress_14_gap_event(struct ble_gap_event *event, void *arg)
+{
+ int bytes_num = 10000;
+ static struct os_mbuf *om = NULL;
+ 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) {
+ MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[14].num);
+ rx_stress_ctx->conn_handle = event->connect.conn_handle;
+ } else {
+ /* Connection failed; resume advertising */
+ MODLOG_DFLT(INFO, "Connection failed; status=%d ",
+ event->connect.status);
+ assert(0);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+
+ rx_stress_ctx->s14_notif_time = rx_stress_ctx->time_sum /
+ rx_stress_ctx->send_num;
+
+ MODLOG_DFLT(INFO, "Average time: %d us\n",
+ rx_stress_ctx->s14_notif_time);
+
+ rx_stress_on_test_finish(14);
+ return 0;
+
+ case BLE_GAP_EVENT_NOTIFY_TX:
+ MODLOG_DFLT(INFO, "Notify TX event\n");
+ assert(event->notify_tx.status == 0);
+ return 0;
+
+ case BLE_GAP_EVENT_SUBSCRIBE:
+ MODLOG_DFLT(INFO, "Subscribe event\n");
+
+ if (event->subscribe.cur_notify) {
+ MODLOG_DFLT(INFO, "Notification subscribed\n");
+
+ ++rx_stress_ctx->send_num;
+
+ /* Notify data pattern */
+ om = ble_hs_mbuf_from_flat(test_6_pattern, bytes_num);
+
+ rc = ble_gattc_notify_custom(rx_stress_ctx->conn_handle,
+ hrs_hrm_handle, om);
+ assert(rc == 0);
+
+ console_printf("\033[0;32m>\033[0m");
+ } else if (event->subscribe.prev_notify) {
+ MODLOG_DFLT(INFO, "Notification unsubscribed\n");
+ } else {
+ MODLOG_DFLT(INFO, "Other subscription\n");
+ }
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+rx_stress_15_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ ++rx_stress_ctx->con_stat[15].attempts_num;
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
+ event->connect.status,
+ ++rx_stress_ctx->con_stat[15].num);
+ } else {
+ MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
+ "status=%d\033[0m\n", event->connect.status);
+ assert(0);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ console_printf("\033[0;32m>\033[0m");
+ if (rx_stress_ctx->con_stat[15].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ rx_stress_on_test_finish(15);
+ } else {
+ /* Connection terminated; resume advertising. */
+ rx_stress_adv_start(TEST_INSTANCE);
+ }
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+/* Advert settings for each test. */
+static struct rx_stress_adv_set rx_stress_adv_sets[] = {
+ {
+ .instance = SWITCHER_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[0],
+ .legacy_pdu = LEGACY_ADVERT,
+ .cb = rx_stress_0_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = SWITCHER_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[0],
+ .legacy_pdu = LEGACY_ADVERT,
+ .cb = rx_stress_0_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[2],
+ .legacy_pdu = LEGACY_ADVERT,
+ .cb = rx_stress_2_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[3],
+ .legacy_pdu = EXTENDED_ADVERT,
+ .cb = rx_stress_3_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[4],
+ .legacy_pdu = LEGACY_ADVERT,
+ .cb = rx_stress_4_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[5],
+ .legacy_pdu = LEGACY_ADVERT,
+ .cb = rx_stress_5_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[6],
+ .legacy_pdu = EXTENDED_ADVERT,
+ .cb = rx_stress_6_gap_event,
+ .pattern_data = test_6_pattern,
+ .pattern_len = 1640,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[7],
+ .legacy_pdu = EXTENDED_ADVERT,
+ .cb = rx_stress_7_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[8],
+ .legacy_pdu = EXTENDED_ADVERT,
+ .cb = rx_stress_8_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[9],
+ .legacy_pdu = EXTENDED_ADVERT,
+ .cb = rx_stress_9_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[10],
+ .legacy_pdu = EXTENDED_ADVERT,
+ .cb = rx_stress_10_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[11],
+ .legacy_pdu = EXTENDED_ADVERT,
+ .cb = rx_stress_11_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[12],
+ .legacy_pdu = EXTENDED_ADVERT,
+ .cb = rx_stress_12_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[13],
+ .legacy_pdu = EXTENDED_ADVERT,
+ .cb = rx_stress_13_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[14],
+ .legacy_pdu = EXTENDED_ADVERT,
+ .cb = rx_stress_14_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+ {
+ .instance = TEST_INSTANCE,
+ .instance_uuid128 = rx_stress_uuid128[15],
+ .legacy_pdu = EXTENDED_ADVERT,
+ .cb = rx_stress_15_gap_event,
+ .pattern_data = NULL,
+ .pattern_len = 0,
+ },
+};
+
+static void
+rx_stress_start(int test_num)
+{
+ int rc;
+
+ /* Init semaphore with 0 tokens. */
+ os_sem_init(&rx_stress_main_sem, 0);
+
+ console_printf("\033[1;36mStart test num %d - ", test_num);
+
+ /* Start test. */
+ switch (test_num) {
+ case 2:
+ console_printf("Stress Connect/Disconnect legacy\033[0m\n");
+ rx_stress_simple_adv(&rx_stress_adv_sets[2]);
+ break;
+ case 3:
+ console_printf("Stress Connect/Disconnect ext adv\033[0m\n");
+ rx_stress_simple_adv(&rx_stress_adv_sets[3]);
+ break;
+ case 4:
+ console_printf("Stress connection params update (TX)\033[0m\n");
+ rx_stress_simple_adv(&rx_stress_adv_sets[4]);
+ break;
+ case 5:
+ console_printf("Stress connection params update (RX)\033[0m\n");
+ rx_stress_simple_adv(&rx_stress_adv_sets[5]);
+ break;
+ case 6:
+ /* Start SWITCHER advert that gives possibility to remotely start
+ * next test advert */
+ console_printf("Stress Scan\033[0m\n");
+ rx_stress_simple_adv(&rx_stress_adv_sets[0]);
+ rx_stress_simple_adv(&rx_stress_adv_sets[6]);
+ break;
+ case 7:
+ console_printf("Stress PHY Update (TX)\033[0m\n");
+ rx_stress_simple_adv(&rx_stress_adv_sets[7]);
+ break;
+ case 8:
+ console_printf("Stress PHY Update (RX)\033[0m\n");
+ rx_stress_simple_adv(&rx_stress_adv_sets[8]);
+ break;
+ case 9:
+ console_printf("Stress multi connection\033[0m\n");
+ rx_stress_simple_adv(&rx_stress_adv_sets[9]);
+ break;
+ case 10:
+ console_printf("Stress L2CAP send\033[0m\n");
+ rc = ble_l2cap_create_server(1, STRESS_COC_MTU,
+ rx_stress_10_l2cap_event, NULL);
+ assert(rc == 0);
+ rx_stress_simple_adv(&rx_stress_adv_sets[10]);
+ break;
+ case 11:
+ console_printf("Stress Advertise/Connect/Disconnect\033[0m\n");
+ rx_stress_simple_adv(&rx_stress_adv_sets[11]);
+ break;
+ case 12:
+ console_printf("Stress GATT indication\033[0m\n");
+ rx_stress_simple_adv(&rx_stress_adv_sets[12]);
+ break;
+ case 13:
+ console_printf("Stress GATT notification\033[0m\n");
+ rx_stress_simple_adv(&rx_stress_adv_sets[13]);
+ break;
+ case 14:
+ console_printf("Stress GATT Subscribe/Notify/Unsubscribe\033[0m\n");
+ rx_stress_simple_adv(&rx_stress_adv_sets[14]);
+ break;
+ case 15:
+ console_printf("Stress Connect/Send/Disconnect\033[0m\n");
+ rx_stress_simple_adv(&rx_stress_adv_sets[15]);
+ break;
+ default:
+ console_printf("\033[0;31mFound test, but do not know how to perform."
+ "\033[0m\n");
+ assert(0);
+ }
+
+ /* Wait for the test to finish. Then 1 token will be released
+ * allowing to pass through semaphore. */
+ os_sem_pend(&rx_stress_main_sem, OS_TIMEOUT_NEVER);
+
+ ble_gap_ext_adv_stop(SWITCHER_INSTANCE);
+
+ stress_clear_ctx_reusable_var(rx_stress_ctx);
+}
+
+static void
+stress_uuid_init()
+{
+ uint8_t i;
+
+ for (i = 0; i < STRESS_UUIDS_NUM; ++i) {
+ /* Fill all 16 bytes of UUID128 */
+ rx_stress_uuid128[i][0] = 0xC0;
+ rx_stress_uuid128[i][1] = 0xDE;
+ rx_stress_uuid128[i][2] = i;
+ memcpy(&rx_stress_uuid128[i][3], MYNEWT_VAL(BLE_STRESS_UUID_BASE), 13);
+ }
+}
+
+static void
+rx_stress_read_command_cb(void)
+{
+ console_printf("Start testing\n");
+ os_sem_release(&rx_stress_main_sem);
+}
+
+static void
+rx_stress_main_task_fn(void *arg)
+{
+ int i;
+
+ stress_uuid_init();
+
+ console_printf("\033[1;36mRX device\033[0m\n");
+ console_printf("Press ENTER to start: \n");
+ console_init(&rx_stress_read_command_cb);
+
+ /* Waite for pressing ENTER in console */
+ os_sem_pend(&rx_stress_main_sem, OS_TIMEOUT_NEVER);
+
+ /* Standard tests perform */
+ for (i = 11; i < STRESS_UUIDS_NUM; ++i) {
+ if (i == 7 || i == 8 || i == 13) {
+ /* 7,8: PHY update tests cause that the device during the next test
+ * will stuck somewhere and will reset. Skip them for now.
+ * 13: Should work after fixing ble_gattc_notify_custom (nimble issue on GitHub)*/
+ continue;
+ }
+ /* Start test. */
+ rx_stress_start(i);
+ }
+
+ /* Print tests results */
+ com_stress_print_report(rx_stress_ctx);
+
+ /* Task should never return */
+ while (1) {
+ }
+}
+
+void
+rx_stress_start_auto()
+{
+ /* Start task that will run all stress tests one by one. */
+ os_task_init(&rx_stress_main_task, "rx_stress_main_task",
+ rx_stress_main_task_fn, NULL, RX_STRESS_MAIN_TASK_PRIO,
+ OS_WAIT_FOREVER, rx_stress_main_task_stack,
+ RX_STRESS_MAIN_TASK_STACK_SIZE);
+}
diff --git a/src/libs/mynewt-nimble/apps/blestress/src/rx_stress.h b/src/libs/mynewt-nimble/apps/blestress/src/rx_stress.h
new file mode 100644
index 00000000..62f84117
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/src/rx_stress.h
@@ -0,0 +1,53 @@
+/*
+ * 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 _BLE_STRESS_RX_H
+#define _BLE_STRESS_RX_H
+
+#include <assert.h>
+#include <string.h>
+#include <console/console.h>
+#include <errno.h>
+#include <nrfx/hal/nrf_aar.h>
+
+/* BLE */
+#include "nimble/ble.h"
+#include "host/ble_hs.h"
+#include "services/gap/ble_svc_gap.h"
+#include "host/ble_gap.h"
+#include <host/ble_l2cap.h>
+
+#include "misc.h"
+#include "stress.h"
+#include "stress_gatt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Executes stress tests one by one.
+ */
+void rx_stress_start_auto();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_BLE_STRESS_RX_H
diff --git a/src/libs/mynewt-nimble/apps/blestress/src/stress.c b/src/libs/mynewt-nimble/apps/blestress/src/stress.c
new file mode 100644
index 00000000..6f5badf0
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/src/stress.c
@@ -0,0 +1,389 @@
+/*
+ * 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 "stress.h"
+
+void
+com_stress_print_report(const struct com_stress_test_ctx *test_ctxs)
+{
+ console_printf("\033[0;32mAll tests completed\033[0m\n");
+ console_printf("Tests results:\n");
+
+ console_printf(
+ "\033[0;33mUse case 1 - Stress Connect -> Connect Cancel: \n\033[0m");
+ console_printf("Con attempts = %d\n", test_ctxs->con_stat[1].attempts_num);
+ console_printf("Con success = %d\n", test_ctxs->con_stat[1].num);
+
+ console_printf(
+ "\033[0;33mUse case 2 - Stress Connect/Disconnect legacy: \n\033[0m");
+ console_printf("Con attempts = %d\n", test_ctxs->con_stat[2].attempts_num);
+ console_printf("Con success = %d\n", test_ctxs->con_stat[2].num);
+
+ console_printf(
+ "\033[0;33mUse case 3 - Stress Connect/Disconnect ext adv: \n\033[0m");
+ console_printf("Con attempts = %d\n", test_ctxs->con_stat[3].attempts_num);
+ console_printf("Con success = %d\n", test_ctxs->con_stat[3].num);
+
+ console_printf(
+ "\033[0;33mUse case 4 - Stress connection params update (TX): \n\033[0m");
+ console_printf("Params updates = %d\n",
+ test_ctxs->con_stat[4].prms_upd_num);
+
+ console_printf(
+ "\033[0;33mUse case 5 - Stress connection params update (RX): \n\033[0m");
+ console_printf("Params updates = %d\n",
+ test_ctxs->con_stat[5].prms_upd_num);
+
+ console_printf("\033[0;33mUse case 6 - Stress Scan: \n\033[0m");
+ console_printf("Received first packets = %d\n",
+ test_ctxs->s6_rcv_adv_first);
+ console_printf("Received all packets = %d\n", test_ctxs->s6_rcv_adv_suc);
+
+ console_printf("\033[0;33mUse case 7 - Stress PHY Update (TX): \n\033[0m");
+ console_printf("PHY updates = %d\n", test_ctxs->con_stat[7].phy_upd_num);
+
+ console_printf("\033[0;33mUse case 8 - Stress PHY Update (RX): \n\033[0m");
+ console_printf("PHY updates = %d\n", test_ctxs->con_stat[8].phy_upd_num);
+
+ console_printf(
+ "\033[0;33mUse case 9 - Stress multi connection: \n\033[0m");
+ console_printf("Max reached num of connections = %d\n",
+ test_ctxs->con_stat[9].max_num);
+
+ console_printf("\033[0;33mUse case 10 - Stress L2CAP send: \n\033[0m");
+ console_printf("Average bit rate = %d\n", test_ctxs->s10_bit_rate);
+ console_printf("Max received MTU = %lld\n", test_ctxs->s10_max_mtu);
+
+ console_printf("\033[0;33mUse case 11 - "
+ "Stress Advertise/Connect/Continue adv \n\033[0m");
+// console_printf(" = %d\n",);
+
+ console_printf("\033[0;33mUse case 12 - "
+ "Stress GATT indication: \n\033[0m");
+ console_printf("Average bit rate = %d\n", test_ctxs->s12_notif_time);
+
+ console_printf("\033[0;33mUse case 13 - "
+ "Stress GATT notification: \n\033[0m");
+ console_printf("Average time = %d\n", test_ctxs->s13_notif_time);
+
+ console_printf("\033[0;33mUse case 14 - "
+ "Stress GATT Subscribe/Notify/Unsubscribe: \n\033[0m");
+ console_printf("Average time = %d\n", test_ctxs->s14_notif_time);
+
+ console_printf("\033[0;33mUse case 15 - "
+ "Stress Connect/Send/Disconnect: \n\033[0m");
+ console_printf("Con num = %d\n", test_ctxs->con_stat[15].num);
+}
+
+void
+stress_clear_ctx_reusable_var(struct com_stress_test_ctx *ctx)
+{
+ ctx->cur_test_id = 0;
+ ctx->dev_addr.type = 0;
+ ctx->dev_addr.type = 0;
+ ctx->conn_handle = 0;
+ ctx->chan = 0;
+ ctx->rcv_data_bytes = 0;
+ ctx->rcv_num = 0;
+ ctx->send_num = 0;
+ ctx->begin_us = 0;
+ ctx->end_us = 0;
+ ctx->time_sum = 0;
+ ctx->bytes_sum = 0;
+ ctx->timeout_flag = 0;
+ ctx->rcv_data_flag = 0;
+}
+
+int
+stress_fill_mbuf_with_pattern(struct os_mbuf *om, uint16_t len)
+{
+ int rc, i, mul, rest;
+
+ mul = len / STRESS_PAT_LEN;
+ rest = len % STRESS_PAT_LEN;
+
+ for (i = 0; i < mul; ++i) {
+ rc = os_mbuf_append(om, &test_6_pattern[29], STRESS_PAT_LEN);
+
+ if (rc) {
+ os_mbuf_free_chain(om);
+ assert(0);
+ }
+ }
+
+ rc = os_mbuf_append(om, &test_6_pattern[29], rest);
+
+ if (rc) {
+ os_mbuf_free_chain(om);
+ assert(0);
+ }
+
+ return rc;
+}
+
+void
+stress_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu)
+{
+ int rc;
+ console_printf("LE CoC SDU received, chan: 0x%08lx, data len %d\n",
+ (uint32_t) chan, OS_MBUF_PKTLEN(sdu));
+
+ rc = os_mbuf_free_chain(sdu);
+ assert(rc == 0);
+
+ /* Get buffer for data chain */
+ sdu = os_msys_get_pkthdr(STRESS_COC_MTU, 0);
+ assert(sdu != NULL);
+
+ /* Receive data chain */
+ rc = ble_l2cap_recv_ready(chan, sdu);
+ assert(rc == 0);
+}
+
+void
+stress_l2cap_coc_accept(uint16_t peer_mtu, struct ble_l2cap_chan *chan)
+{
+ struct os_mbuf *sdu_rx;
+ int rc;
+
+ console_printf("LE CoC accepting, chan: 0x%08lx, peer_mtu %d\n",
+ (uint32_t) chan, peer_mtu);
+
+ sdu_rx = os_msys_get_pkthdr(STRESS_COC_MTU, 0);
+ assert(sdu_rx != NULL);
+
+ rc = ble_l2cap_recv_ready(chan, sdu_rx);
+ assert(rc == 0);
+}
+
+void
+stress_start_timer(uint32_t timeout_ms, os_event_fn *ev_cb)
+{
+ int rc;
+ os_callout_stop(&stress_timer_callout);
+
+ os_callout_init(&stress_timer_callout, os_eventq_dflt_get(), ev_cb, NULL);
+
+ rc = os_callout_reset(&stress_timer_callout,
+ os_time_ms_to_ticks32(timeout_ms));
+
+ assert(rc == 0);
+}
+
+int64_t
+stress_calc_bit_rate(int64_t us, int64_t bytes_num)
+{
+ int bit_rate;
+
+ /* Multiply by 1000000 so you don't lose accuracy */
+ bit_rate = 1000000 * bytes_num / us;
+
+ return bit_rate;
+}
+
+static int
+stress_disc_dsc_fn(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ uint16_t chr_val_handle,
+ const struct ble_gatt_dsc *dsc,
+ void *arg)
+{
+ struct stress_gatt_search_ctx *search_ctx;
+ static bool found = false;
+
+ search_ctx = (struct stress_gatt_search_ctx *) arg;
+
+ if (error->status == 0) {
+ if (!ble_uuid_cmp(&dsc->uuid.u, &search_ctx->dsc_uuid.u) && !found) {
+ MODLOG_DFLT(INFO, "Found chr descriptor\n");
+ search_ctx->dsc_handle = dsc->handle;
+ MODLOG_DFLT(INFO, "uuid=%#06x; handle=%#06x", dsc->uuid.u16.value,
+ dsc->handle);
+ found = true;
+ }
+ return 0;
+ }
+
+ if (error->status == BLE_HS_EDONE) {
+ MODLOG_DFLT(INFO, "Done descriptor discovery\n");
+
+ if (found) {
+ found = false;
+ search_ctx->disc_end_fn(search_ctx);
+ return 0;
+ }
+
+ MODLOG_DFLT(ERROR, "\033[0;31mDid not find particular descriptor"
+ "\033[0m\n");
+ return 0;
+ }
+
+ MODLOG_DFLT(ERROR, "\033[0;31mError during descriptor discovery"
+ "\033[0m\n");
+ assert(0);
+ return 0;
+}
+
+static int
+stress_disc_chr_fn(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ const struct ble_gatt_chr *chr, void *arg)
+{
+ int rc;
+ struct stress_gatt_search_ctx *search_ctx;
+ static bool found = false;
+
+ search_ctx = (struct stress_gatt_search_ctx *) arg;
+
+ if (error->status == 0) {
+ MODLOG_DFLT(INFO, "Found characteristic\n");
+ search_ctx->chr_start_handle = chr->val_handle;
+ found = true;
+ return 0;
+ }
+
+ if (error->status == BLE_HS_EDONE) {
+ MODLOG_DFLT(INFO, "Done characteristic discovery\n");
+
+ if (found) {
+ found = false;
+
+ if (search_ctx->search_goal == STRESS_FIND_CHR) {
+ search_ctx->disc_end_fn(search_ctx);
+ return 0;
+ }
+
+ rc = ble_gattc_disc_all_dscs(conn_handle,
+ search_ctx->chr_start_handle,
+ search_ctx->srv_end_handle,
+ &stress_disc_dsc_fn, search_ctx);
+ assert(rc == 0);
+ return 0;
+ }
+
+ MODLOG_DFLT(ERROR, "\033[0;31mDid not find particular "
+ "characteristic\033[0m\n");
+ return 0;
+ }
+
+ MODLOG_DFLT(ERROR,
+ "\033[0;31mError during characteristic discovery\033[0m\n");
+ assert(0);
+ return 0;
+}
+
+static int
+stress_disc_svc_fn(uint16_t conn_handle, const struct ble_gatt_error *error,
+ const struct ble_gatt_svc *service, void *arg)
+{
+ int rc;
+ struct stress_gatt_search_ctx *search_ctx = NULL;
+ static bool found = false;
+
+ search_ctx = (struct stress_gatt_search_ctx *) arg;
+
+ if (error->status == 0) {
+ MODLOG_DFLT(INFO, "Found service\n");
+ search_ctx->srv_start_handle = service->start_handle;
+ search_ctx->srv_end_handle = service->end_handle;
+ found = true;
+ return 0;
+ }
+
+ if (error->status == BLE_HS_EDONE) {
+ MODLOG_DFLT(INFO, "Done service discovery\n");
+
+ if (found) {
+ found = false;
+ if (search_ctx->search_goal == STRESS_FIND_SRV) {
+ search_ctx->disc_end_fn(search_ctx);
+ return 0;
+ }
+
+ rc = ble_gattc_disc_chrs_by_uuid(conn_handle,
+ search_ctx->srv_start_handle,
+ search_ctx->srv_end_handle,
+ &search_ctx->chr_uuid.u,
+ &stress_disc_chr_fn,
+ search_ctx);
+ MODLOG_DFLT(INFO, "rc=%d\n", rc);
+ assert(rc == 0);
+ return 0;
+ }
+
+ MODLOG_DFLT(ERROR,
+ "\033[0;31mDid not find particular service\033[0m\n");
+ return 0;
+ }
+
+ MODLOG_DFLT(ERROR, "\033[0;31mError during service discovery\033[0m\n");
+ assert(0);
+ return 0;
+}
+
+static void
+stress_gatt_find_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
+ const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid,
+ stress_gatt_disc_end_fn *disc_end_fn,
+ uint8_t search_goal)
+{
+ static struct stress_gatt_search_ctx search_ctx;
+ int rc;
+
+ search_ctx.conn_handle = conn_handle;
+ ble_uuid_copy((ble_uuid_any_t *) &search_ctx.srv_uuid, srv_uuid);
+ ble_uuid_copy((ble_uuid_any_t *) &search_ctx.chr_uuid, chr_uuid);
+ ble_uuid_copy((ble_uuid_any_t *) &search_ctx.dsc_uuid, dsc_uuid);
+ search_ctx.disc_end_fn = disc_end_fn;
+ search_ctx.search_goal = search_goal;
+ search_ctx.srv_start_handle = 0;
+ search_ctx.srv_end_handle = 0;
+ search_ctx.chr_start_handle = 0;
+ search_ctx.dsc_handle = 0;
+
+ rc = ble_gattc_disc_svc_by_uuid(conn_handle, srv_uuid, &stress_disc_svc_fn,
+ &search_ctx);
+ assert(rc == 0);
+}
+
+void
+stress_find_svc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
+ stress_gatt_disc_end_fn *disc_end_fn)
+{
+ stress_gatt_find_handle(conn_handle, srv_uuid, NULL, NULL, disc_end_fn,
+ STRESS_FIND_SRV);
+}
+
+void
+stress_find_chr_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
+ const ble_uuid_t *chr_uuid,
+ stress_gatt_disc_end_fn *disc_end_fn)
+{
+ stress_gatt_find_handle(conn_handle, srv_uuid, chr_uuid, NULL, disc_end_fn,
+ STRESS_FIND_CHR);
+}
+
+void
+stress_find_dsc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
+ const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid,
+ stress_gatt_disc_end_fn *disc_end_fn)
+{
+ stress_gatt_find_handle(conn_handle, srv_uuid, chr_uuid, dsc_uuid,
+ disc_end_fn, STRESS_FIND_DSC);
+}
diff --git a/src/libs/mynewt-nimble/apps/blestress/src/stress.h b/src/libs/mynewt-nimble/apps/blestress/src/stress.h
new file mode 100644
index 00000000..91ab4f47
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/src/stress.h
@@ -0,0 +1,375 @@
+/*
+ * 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 BLE_TGT_STRESS_H
+#define BLE_TGT_STRESS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <nimble/ble.h>
+#include <modlog/modlog.h>
+#include <os/mynewt.h>
+#include <console/console.h>
+#include <host/ble_l2cap.h>
+#include <host/ble_gatt.h>
+#include <host/ble_gap.h>
+#include <assert.h>
+
+#define STRESS_UUIDS_NUM (16)
+
+/* No preferred PHY */
+#define TX_PHY_MASK 0
+#define RX_PHY_MASK 0
+/* L2CAP SDU */
+#define STRESS_COC_MTU (64000)
+
+#define STRESS_FIND_SRV 1
+#define STRESS_FIND_CHR 2
+#define STRESS_FIND_DSC 3
+
+struct os_callout stress_timer_callout;
+struct stress_gatt_search_ctx;
+typedef void stress_gatt_disc_end_fn(struct stress_gatt_search_ctx *search_ctx);
+
+struct stress_gatt_search_ctx {
+ /* Connection handle */
+ uint16_t conn_handle;
+
+ /* Wanted service uuid */
+ ble_uuid16_t srv_uuid;
+
+ /* Wanted characteristic uuid */
+ ble_uuid16_t chr_uuid;
+
+ /* Wanted descriptor uuid */
+ ble_uuid16_t dsc_uuid;
+
+ /* Search goal: 1 - service, 2 - characteristic, 3 - descriptor */
+ uint8_t search_goal;
+
+ /* Callback after reaching the goal */
+ stress_gatt_disc_end_fn *disc_end_fn;
+
+ /* Service start handle */
+ uint16_t srv_start_handle;
+
+ /* Service end handle */
+ uint16_t srv_end_handle;
+
+ /* Characteristic start handle */
+ uint16_t chr_start_handle;
+
+ /* Descriptor handle */
+ uint16_t dsc_handle;
+};
+
+struct stress_con_stat {
+ /* Number of successful connection attempts */
+ int num;
+
+ /* Max number of established connections */
+ int max_num;
+
+ /* Number connection attempts */
+ int attempts_num;
+
+ /* Number of params updates */
+ int prms_upd_num;
+
+ /* Number of PHY updates */
+ int phy_upd_num;
+};
+
+/* Common stress test context.
+ * (Reusable) - auxiliary variable
+ * (Stress x) - variable contains result of x stress test */
+struct com_stress_test_ctx {
+ /* Which of tests are completed already. Each element for different
+ * stress test. */
+ bool completed[STRESS_UUIDS_NUM];
+
+ /* Connection stats. Each element for different stress test. */
+ struct stress_con_stat con_stat[STRESS_UUIDS_NUM];
+
+/* Reusable variables */
+
+ /* Stress test number */
+ int cur_test_id;
+
+ /* Instance address */
+ ble_addr_t dev_addr;
+
+ /* Connection handler */
+ uint16_t conn_handle;
+
+ /* L2CAP channel (Reusable) */
+ struct ble_l2cap_chan * chan;
+
+ /* Size of received data */
+ int64_t rcv_data_bytes;
+
+ /* Number of received packages */
+ int rcv_num;
+
+ /* Number of send actions */
+ int send_num;
+
+ /* Variables for bit rate measurement */
+ int64_t begin_us;
+ int64_t end_us;
+ int64_t time_sum;
+ int64_t bytes_sum;
+
+ /* Timeout flag */
+ bool timeout_flag;
+
+ /* Data received flag */
+ bool rcv_data_flag;
+
+ uint16_t start_handle;
+ uint16_t end_handle;
+ uint16_t dsc_handle;
+
+/* Variables for test results */
+ /* Reached timeout of scanning for test */
+ bool scan_timeout;
+
+ /* Number of received first packets of adverts */
+ int s6_rcv_adv_first;
+
+ /* Number of full received adverts */
+ int s6_rcv_adv_suc;
+
+ /* L2CAP Send Bit Rate */
+ int s10_bit_rate;
+
+ /* Average indication time */
+ int s12_notif_time;
+
+ /* Average notification time */
+ int s13_notif_time;
+
+ /* Average notification time */
+ int s14_notif_time;
+
+ /* Max size of received MTU */
+ int64_t s10_max_mtu;
+};
+
+#define STRESS_PAT_LEN 1650
+
+static const uint8_t test_6_pattern[STRESS_PAT_LEN] = {
+ /* Random data */
+ 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0a,
+ 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14,
+ 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1e,
+ 0x00, 0x20, 0x00, 0x22, 0x00, 0x24, 0x00, 0x26, 0x00, 0x28,
+ 0x00, 0x2a, 0x00, 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32,
+ 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c,
+ 0x00, 0x3e, 0x00, 0x40, 0x00, 0x42, 0x00, 0x44, 0x00, 0x46,
+ 0x00, 0x48, 0x00, 0x4a, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50,
+ 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x58, 0x00, 0x5a,
+ 0x00, 0x5c, 0x00, 0x5e, 0x00, 0x60, 0x00, 0x62, 0x00, 0x64,
+ 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, 0x6c, 0x00, 0x6e,
+ 0x00, 0x70, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x78,
+ 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x82,
+ 0x00, 0x84, 0x00, 0x86, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x8c,
+ 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96,
+ 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa0,
+ 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa,
+ 0x00, 0xac, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4,
+ 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe,
+ 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc8,
+ 0x00, 0xca, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd2,
+ 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdc,
+ 0x00, 0xde, 0x00, 0xe0, 0x00, 0xe2, 0x00, 0xe4, 0x00, 0xe6,
+ 0x00, 0xe8, 0x00, 0xea, 0x00, 0xec, 0x00, 0xee, 0x00, 0xf0,
+ 0x00, 0xf2, 0x00, 0xf4, 0x00, 0xf6, 0x00, 0xf8, 0x00, 0xfa,
+ 0x00, 0xfc, 0x00, 0xfe, 0x01, 0x01, 0x01, 0x03, 0x01, 0x05,
+ 0x01, 0x07, 0x01, 0x09, 0x01, 0x0b, 0x01, 0x0d, 0x01, 0x0f,
+ 0x01, 0x11, 0x01, 0x13, 0x01, 0x15, 0x01, 0x17, 0x01, 0x19,
+ 0x01, 0x1b, 0x01, 0x1d, 0x01, 0x1f, 0x01, 0x21, 0x01, 0x23,
+ 0x01, 0x25, 0x01, 0x27, 0x01, 0x29, 0x01, 0x2b, 0x01, 0x2d,
+ 0x01, 0x2f, 0x01, 0x31, 0x01, 0x33, 0x01, 0x35, 0x01, 0x37,
+ 0x01, 0x39, 0x01, 0x3b, 0x01, 0x3d, 0x01, 0x3f, 0x01, 0x41,
+ 0x01, 0x43, 0x01, 0x45, 0x01, 0x47, 0x01, 0x49, 0x01, 0x4b,
+ 0x01, 0x4d, 0x01, 0x4f, 0x01, 0x51, 0x01, 0x53, 0x01, 0x55,
+ 0x01, 0x57, 0x01, 0x59, 0x01, 0x5b, 0x01, 0x5d, 0x01, 0x5f,
+ 0x01, 0x61, 0x01, 0x63, 0x01, 0x65, 0x01, 0x67, 0x01, 0x69,
+ 0x01, 0x6b, 0x01, 0x6d, 0x01, 0x6f, 0x01, 0x71, 0x01, 0x73,
+ 0x01, 0x75, 0x01, 0x77, 0x01, 0x79, 0x01, 0x7b, 0x01, 0x7d,
+ 0x01, 0x7f, 0x01, 0x81, 0x01, 0x83, 0x01, 0x85, 0x01, 0x87,
+ 0x01, 0x89, 0x01, 0x8b, 0x01, 0x8d, 0x01, 0x8f, 0x01, 0x91,
+ 0x01, 0x93, 0x01, 0x95, 0x01, 0x97, 0x01, 0x99, 0x01, 0x9b,
+ 0x01, 0x9d, 0x01, 0x9f, 0x01, 0xa1, 0x01, 0xa3, 0x01, 0xa5,
+ 0x01, 0xa7, 0x01, 0xa9, 0x01, 0xab, 0x01, 0xad, 0x01, 0xaf,
+ 0x01, 0xb1, 0x01, 0xb3, 0x01, 0xb5, 0x01, 0xb7, 0x01, 0xb9,
+ 0x01, 0xbb, 0x01, 0xbd, 0x01, 0xbf, 0x01, 0xc1, 0x01, 0xc3,
+ 0x01, 0xc5, 0x01, 0xc7, 0x01, 0xc9, 0x01, 0xcb, 0x01, 0xcd,
+ 0x01, 0xcf, 0x01, 0xd1, 0x01, 0xd3, 0x01, 0xd5, 0x01, 0xd7,
+ 0x01, 0xd9, 0x01, 0xdb, 0x01, 0xdd, 0x01, 0xdf, 0x01, 0xe1,
+ 0x01, 0xe3, 0x01, 0xe5, 0x01, 0xe7, 0x01, 0xe9, 0x01, 0xeb,
+ 0x01, 0xed, 0x01, 0xef, 0x01, 0xf1, 0x01, 0xf3, 0x01, 0xf5,
+ 0x01, 0xf7, 0x01, 0xf9, 0x01, 0xfb, 0x01, 0xfd, 0x02, 0x00,
+ 0x02, 0x02, 0x02, 0x04, 0x02, 0x06, 0x02, 0x08, 0x02, 0x0a,
+ 0x02, 0x0c, 0x02, 0x0e, 0x02, 0x10, 0x02, 0x12, 0x02, 0x14,
+ 0x02, 0x16, 0x02, 0x18, 0x02, 0x1a, 0x02, 0x1c, 0x02, 0x1e,
+ 0x02, 0x20, 0x02, 0x22, 0x02, 0x24, 0x02, 0x26, 0x02, 0x28,
+ 0x02, 0x2a, 0x02, 0x2c, 0x02, 0x2e, 0x02, 0x30, 0x02, 0x32,
+ 0x02, 0x34, 0x02, 0x36, 0x02, 0x38, 0x02, 0x3a, 0x02, 0x3c,
+ 0x02, 0x3e, 0x02, 0x40, 0x02, 0x42, 0x02, 0x44, 0x02, 0x46,
+ 0x02, 0x48, 0x02, 0x4a, 0x02, 0x4c, 0x02, 0x4e, 0x02, 0x50,
+ 0x02, 0x52, 0x02, 0x54, 0x02, 0x56, 0x02, 0x58, 0x02, 0x5a,
+ 0x02, 0x5c, 0x02, 0x5e, 0x02, 0x60, 0x02, 0x62, 0x02, 0x64,
+ 0x02, 0x66, 0x02, 0x68, 0x02, 0x6a, 0x02, 0x6c, 0x02, 0x6e,
+ 0x02, 0x70, 0x02, 0x72, 0x02, 0x74, 0x02, 0x76, 0x02, 0x78,
+ 0x02, 0x7a, 0x02, 0x7c, 0x02, 0x7e, 0x02, 0x80, 0x02, 0x82,
+ 0x02, 0x84, 0x02, 0x86, 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c,
+ 0x02, 0x8e, 0x02, 0x90, 0x02, 0x92, 0x02, 0x94, 0x02, 0x96,
+ 0x02, 0x98, 0x02, 0x9a, 0x02, 0x9c, 0x02, 0x9e, 0x02, 0xa0,
+ 0x02, 0xa2, 0x02, 0xa4, 0x02, 0xa6, 0x02, 0xa8, 0x02, 0xaa,
+ 0x02, 0xac, 0x02, 0xae, 0x02, 0xb0, 0x02, 0xb2, 0x02, 0xb4,
+ 0x02, 0xb6, 0x02, 0xb8, 0x02, 0xba, 0x02, 0xbc, 0x02, 0xbe,
+ 0x02, 0xc0, 0x02, 0xc2, 0x02, 0xc4, 0x02, 0xc6, 0x02, 0xc8,
+ 0x02, 0xca, 0x02, 0xcc, 0x02, 0xce, 0x02, 0xd0, 0x02, 0xd2,
+ 0x02, 0xd4, 0x02, 0xd6, 0x02, 0xd8, 0x02, 0xda, 0x02, 0xdc,
+ 0x02, 0xde, 0x02, 0xe0, 0x02, 0xe2, 0x02, 0xe4, 0x02, 0xe6,
+ 0x02, 0xe8, 0x02, 0xea, 0x02, 0xec, 0x02, 0xee, 0x02, 0xf0,
+ 0x02, 0xf2, 0x02, 0xf4, 0x02, 0xf6, 0x02, 0xf8, 0x02, 0xfa,
+ 0x02, 0xfc, 0x02, 0xfe, 0x03, 0x01, 0x03, 0x03, 0x03, 0x05,
+ 0x03, 0x07, 0x03, 0x09, 0x03, 0x0b, 0x03, 0x0d, 0x03, 0x0f,
+ 0x03, 0x11, 0x03, 0x13, 0x03, 0x15, 0x03, 0x17, 0x03, 0x19,
+ 0x03, 0x1b, 0x03, 0x1d, 0x03, 0x1f, 0x03, 0x21, 0x03, 0x23,
+ 0x03, 0x25, 0x03, 0x27, 0x03, 0x29, 0x03, 0x2b, 0x03, 0x2d,
+ 0x03, 0x2f, 0x03, 0x31, 0x03, 0x33, 0x03, 0x35, 0x03, 0x37,
+ 0x03, 0x39, 0x03, 0x3b, 0x03, 0x3d, 0x03, 0x3f, 0x03, 0x41,
+ 0x03, 0x43, 0x03, 0x45, 0x03, 0x47, 0x03, 0x49, 0x03, 0x4b,
+ 0x03, 0x4d, 0x03, 0x4f, 0x03, 0x51, 0x03, 0x53, 0x03, 0x55,
+ 0x03, 0x57, 0x03, 0x59, 0x03, 0x5b, 0x03, 0x5d, 0x03, 0x5f,
+ 0x03, 0x61, 0x03, 0x63, 0x03, 0x65, 0x03, 0x67, 0x03, 0x69,
+ 0x03, 0x6b, 0x03, 0x6d, 0x03, 0x6f, 0x03, 0x71, 0x03, 0x73,
+ 0x03, 0x75, 0x03, 0x77, 0x03, 0x79, 0x03, 0x7b, 0x03, 0x7d,
+ 0x03, 0x7f, 0x03, 0x81, 0x03, 0x83, 0x03, 0x85, 0x03, 0x87,
+ 0x03, 0x89, 0x03, 0x8b, 0x03, 0x8d, 0x03, 0x8f, 0x03, 0x91,
+ 0x03, 0x93, 0x03, 0x95, 0x03, 0x97, 0x03, 0x99, 0x03, 0x9b,
+ 0x03, 0x9d, 0x03, 0x9f, 0x03, 0xa1, 0x03, 0xa3, 0x03, 0xa5,
+ 0x03, 0xa7, 0x03, 0xa9, 0x03, 0xab, 0x03, 0xad, 0x03, 0xaf,
+ 0x03, 0xb1, 0x03, 0xb3, 0x03, 0xb5, 0x03, 0xb7, 0x03, 0xb9,
+ 0x03, 0xbb, 0x03, 0xbd, 0x03, 0xbf, 0x03, 0xc1, 0x03, 0xc3,
+ 0x03, 0xc5, 0x03, 0xc7, 0x03, 0xc9, 0x03, 0xcb, 0x03, 0xcd,
+ 0x03, 0xcf, 0x03, 0xd1, 0x03, 0xd3, 0x03, 0xd5, 0x03, 0xd7,
+ 0x03, 0xd9, 0x03, 0xdb, 0x03, 0xdd, 0x03, 0xdf, 0x03, 0xe1,
+ 0x03, 0xe3, 0x03, 0xe5, 0x03, 0xe7, 0x03, 0xe9, 0x03, 0xeb,
+ 0x03, 0xed, 0x03, 0xef, 0x03, 0xf1, 0x03, 0xf3, 0x03, 0xf5,
+ 0x03, 0xf7, 0x03, 0xf9, 0x03, 0xfb, 0x03, 0xfd, 0x04, 0x00,
+ 0x04, 0x02, 0x04, 0x04, 0x04, 0x06, 0x04, 0x08, 0x04, 0x0a,
+ 0x04, 0x0c, 0x04, 0x0e, 0x04, 0x10, 0x04, 0x12, 0x04, 0x14,
+ 0x04, 0x16, 0x04, 0x18, 0x04, 0x1a, 0x04, 0x1c, 0x04, 0x1e,
+ 0x04, 0x20, 0x04, 0x22, 0x04, 0x24, 0x04, 0x26, 0x04, 0x28,
+ 0x04, 0x2a, 0x04, 0x2c, 0x04, 0x2e, 0x04, 0x30, 0x04, 0x32,
+ 0x04, 0x34, 0x04, 0x36, 0x04, 0x38, 0x04, 0x3a, 0x04, 0x3c,
+ 0x04, 0x3e, 0x04, 0x40, 0x04, 0x42, 0x04, 0x44, 0x04, 0x46,
+ 0x04, 0x48, 0x04, 0x4a, 0x04, 0x4c, 0x04, 0x4e, 0x04, 0x50,
+ 0x04, 0x52, 0x04, 0x54, 0x04, 0x56, 0x04, 0x58, 0x04, 0x5a,
+ 0x04, 0x5c, 0x04, 0x5e, 0x04, 0x60, 0x04, 0x62, 0x04, 0x64,
+ 0x04, 0x66, 0x04, 0x68, 0x04, 0x6a, 0x04, 0x6c, 0x04, 0x6e,
+ 0x04, 0x70, 0x04, 0x72, 0x04, 0x74, 0x04, 0x76, 0x04, 0x78,
+ 0x04, 0x7a, 0x04, 0x7c, 0x04, 0x7e, 0x04, 0x80, 0x04, 0x82,
+ 0x04, 0x84, 0x04, 0x86, 0x04, 0x88, 0x04, 0x8a, 0x04, 0x8c,
+ 0x04, 0x8e, 0x04, 0x90, 0x04, 0x92, 0x04, 0x94, 0x04, 0x96,
+ 0x04, 0x98, 0x04, 0x9a, 0x04, 0x9c, 0x04, 0x9e, 0x04, 0xa0,
+ 0x04, 0xa2, 0x04, 0xa4, 0x04, 0xa6, 0x04, 0xa8, 0x04, 0xaa,
+ 0x04, 0xac, 0x04, 0xae, 0x04, 0xb0, 0x04, 0xb2, 0x04, 0xb4,
+ 0x04, 0xb6, 0x04, 0xb8, 0x04, 0xba, 0x04, 0xbc, 0x04, 0xbe,
+ 0x04, 0xc0, 0x04, 0xc2, 0x04, 0xc4, 0x04, 0xc6, 0x04, 0xc8,
+ 0x04, 0xca, 0x04, 0xcc, 0x04, 0xce, 0x04, 0xd0, 0x04, 0xd2,
+ 0x04, 0xd4, 0x04, 0xd6, 0x04, 0xd8, 0x04, 0xda, 0x04, 0xdc,
+ 0x04, 0xde, 0x04, 0xe0, 0x04, 0xe2, 0x04, 0xe4, 0x04, 0xe6,
+ 0x04, 0xe8, 0x04, 0xea, 0x04, 0xec, 0x04, 0xee, 0x04, 0xf0,
+ 0x04, 0xf2, 0x04, 0xf4, 0x04, 0xf6, 0x04, 0xf8, 0x04, 0xfa,
+ 0x04, 0xfc, 0x04, 0xfe, 0x05, 0x01, 0x05, 0x03, 0x05, 0x05,
+ 0x05, 0x07, 0x05, 0x09, 0x05, 0x0b, 0x05, 0x0d, 0x05, 0x0f,
+ 0x05, 0x11, 0x05, 0x13, 0x05, 0x15, 0x05, 0x17, 0x05, 0x19,
+ 0x05, 0x1b, 0x05, 0x1d, 0x05, 0x1f, 0x05, 0x21, 0x05, 0x23,
+ 0x05, 0x25, 0x05, 0x27, 0x05, 0x29, 0x05, 0x2b, 0x05, 0x2d,
+ 0x05, 0x2f, 0x05, 0x31, 0x05, 0x33, 0x05, 0x35, 0x05, 0x37,
+ 0x05, 0x39, 0x05, 0x3b, 0x05, 0x3d, 0x05, 0x3f, 0x05, 0x41,
+ 0x05, 0x43, 0x05, 0x45, 0x05, 0x47, 0x05, 0x49, 0x05, 0x4b,
+ 0x05, 0x4d, 0x05, 0x4f, 0x05, 0x51, 0x05, 0x53, 0x05, 0x55,
+ 0x05, 0x57, 0x05, 0x59, 0x05, 0x5b, 0x05, 0x5d, 0x05, 0x5f,
+ 0x05, 0x61, 0x05, 0x63, 0x05, 0x65, 0x05, 0x67, 0x05, 0x69,
+ 0x05, 0x6b, 0x05, 0x6d, 0x05, 0x6f, 0x05, 0x71, 0x05, 0x73,
+ 0x05, 0x75, 0x05, 0x77, 0x05, 0x79, 0x05, 0x7b, 0x05, 0x7d,
+ 0x05, 0x7f, 0x05, 0x81, 0x05, 0x83, 0x05, 0x85, 0x05, 0x87,
+ 0x05, 0x89, 0x05, 0x8b, 0x05, 0x8d, 0x05, 0x8f, 0x05, 0x91,
+ 0x05, 0x93, 0x05, 0x95, 0x05, 0x97, 0x05, 0x99, 0x05, 0x9b,
+ 0x05, 0x9d, 0x05, 0x9f, 0x05, 0xa1, 0x05, 0xa3, 0x05, 0xa5,
+ 0x05, 0xa7, 0x05, 0xa9, 0x05, 0xab, 0x05, 0xad, 0x05, 0xaf,
+ 0x05, 0xb1, 0x05, 0xb3, 0x05, 0xb5, 0x05, 0xb7, 0x05, 0xb9,
+ 0x05, 0xbb, 0x05, 0xbd, 0x05, 0xbf, 0x05, 0xc1, 0x05, 0xc3,
+ 0x05, 0xc5, 0x05, 0xc7, 0x05, 0xc9, 0x05, 0xcb, 0x05, 0xcd,
+ 0x05, 0xcf, 0x05, 0xd1, 0x05, 0xd3, 0x05, 0xd5, 0x05, 0xd7,
+ 0x05, 0xd9, 0x05, 0xdb, 0x05, 0xdd, 0x05, 0xdf, 0x05, 0xe1,
+ 0x05, 0xe3, 0x05, 0xe5, 0x05, 0xe7, 0x05, 0xe9, 0x05, 0xeb,
+ 0x05, 0xed, 0x05, 0xef, 0x05, 0xf1, 0x05, 0xf3, 0x05, 0xf5,
+ 0x05, 0xf7, 0x05, 0xf9, 0x05, 0xfb, 0x05, 0xfd, 0x06, 0x00,
+ 0x06, 0x02, 0x06, 0x04, 0x06, 0x06, 0x06, 0x08, 0x06, 0x0a,
+ 0x06, 0x0c, 0x06, 0x0e, 0x06, 0x10, 0x06, 0x12, 0x06, 0x14,
+ 0x06, 0x16, 0x06, 0x18, 0x06, 0x1a, 0x06, 0x1c, 0x06, 0x1e,
+ 0x06, 0x20, 0x06, 0x22, 0x06, 0x24, 0x06, 0x26, 0x06, 0x28,
+ 0x06, 0x2a, 0x06, 0x2c, 0x06, 0x2e, 0x06, 0x30, 0x06, 0x32,
+ 0x06, 0x34, 0x06, 0x36, 0x06, 0x38, 0x06, 0x3a, 0x06, 0x3c,
+ 0x06, 0x3e, 0x06, 0x40, 0x06, 0x42, 0x06, 0x44, 0x06, 0x46,
+ 0x06, 0x48, 0x06, 0x4a, 0x06, 0x4c, 0x06, 0x4e, 0x06, 0x50,
+ 0x06, 0x52, 0x06, 0x54, 0x06, 0x56, 0x06, 0x58, 0x06, 0x5a,
+ 0x06, 0x5c, 0x06, 0x5e, 0x06, 0x60, 0x06, 0x62, 0x06, 0x64,
+ 0x06, 0x66, 0x06, 0x68, 0x06, 0x6a, 0x06, 0x6c, 0x06, 0x6e,
+ 0x06, 0x70, 0x06, 0x72, 0x06, 0x74, 0x06, 0x76, 0x06, 0x78,
+};
+
+void stress_clear_ctx_reusable_var(struct com_stress_test_ctx *ctx);
+
+void com_stress_print_report(const struct com_stress_test_ctx test_ctxs[]);
+
+int stress_fill_mbuf_with_pattern(struct os_mbuf *om, uint16_t len);
+
+void stress_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu);
+
+void stress_l2cap_coc_accept(uint16_t peer_mtu, struct ble_l2cap_chan *chan);
+
+void stress_start_timer(uint32_t timeout_ms, os_event_fn *ev_cb);
+
+int64_t stress_calc_bit_rate(int64_t us, int64_t bytes_num);
+
+void stress_find_svc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
+ stress_gatt_disc_end_fn *disc_end_fn);
+void stress_find_chr_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
+ const ble_uuid_t *chr_uuid,
+ stress_gatt_disc_end_fn *disc_end_fn);
+
+void stress_find_dsc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
+ const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid,
+ stress_gatt_disc_end_fn *disc_end_fn);
+#ifdef __cplusplus
+}
+#endif
+
+#endif //BLE_TGT_STRESS_H
diff --git a/src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.c b/src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.c
new file mode 100644
index 00000000..a6d845c5
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.c
@@ -0,0 +1,155 @@
+/*
+ * 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 "stress_gatt.h"
+
+uint16_t hrs_hrm_handle = 0xffff;
+
+static int
+stress_gatt_access_cb(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg);
+
+static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
+ {
+ /* Service: Heart-rate */
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = BLE_UUID16_DECLARE(STRESS_GATT_UUID),
+ .characteristics = (struct ble_gatt_chr_def[]) {
+ {
+ /* Characteristic: read test */
+ .uuid = BLE_UUID16_DECLARE(STRESS_GATT_READ_UUID),
+ .access_cb = stress_gatt_access_cb,
+ .val_handle = &hrs_hrm_handle,
+ .flags = BLE_GATT_CHR_F_READ,
+ },
+ {
+ /* Characteristic: write test */
+ .uuid = BLE_UUID16_DECLARE(STRESS_GATT_WRITE_UUID),
+ .access_cb = stress_gatt_access_cb,
+ .val_handle = &hrs_hrm_handle,
+ .flags = BLE_GATT_CHR_F_WRITE,
+ },
+ {
+ /* Characteristic: notify test */
+ .uuid = BLE_UUID16_DECLARE(STRESS_GATT_NOTIFY_UUID),
+ .access_cb = stress_gatt_access_cb,
+ .val_handle = &hrs_hrm_handle,
+ .flags = BLE_GATT_CHR_F_NOTIFY,
+ },
+ {
+ /* Characteristic: indicate test */
+ .uuid = BLE_UUID16_DECLARE(STRESS_GATT_INDICATE_UUID),
+ .access_cb = stress_gatt_access_cb,
+ .val_handle = &hrs_hrm_handle,
+ .flags = BLE_GATT_CHR_F_INDICATE,
+ },
+ {
+ 0, /* No more characteristics in this service */
+ },}
+ },
+ {
+ 0, /* No more services */
+ },
+};
+
+static int
+stress_gatt_access_cb(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+ /* Sensor location, set to "Chest" */
+ static uint8_t chr_value[] = "Hello";
+ uint16_t uuid;
+ int rc;
+
+ //chr_value = (uint8_t)rand() % 256;
+ uuid = ble_uuid_u16(ctxt->chr->uuid);
+
+ switch(uuid){
+ case STRESS_GATT_READ_UUID:
+ MODLOG_DFLT(INFO, "GATT Read event\n");
+ rc = os_mbuf_append(ctxt->om, &chr_value, sizeof(chr_value));
+ assert(rc == 0);
+ //return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ return 0;
+ case STRESS_GATT_WRITE_UUID:
+ MODLOG_DFLT(INFO, "GATT Write event\n");
+ print_mbuf(ctxt->om);
+ return 0;
+ case STRESS_GATT_NOTIFY_UUID:
+ MODLOG_DFLT(INFO, "GATT Notify event\n");
+ return 0;
+ case STRESS_GATT_INDICATE_UUID:
+ MODLOG_DFLT(INFO, "GATT Indicate event\n");
+ return 0;
+ default:
+ MODLOG_DFLT(ERROR, "GATT UUID does not exist\n");
+ assert(0);
+ 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:
+ MODLOG_DFLT(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:
+ MODLOG_DFLT(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:
+ MODLOG_DFLT(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;
+}
diff --git a/src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.h b/src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.h
new file mode 100644
index 00000000..3344fe2d
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.h
@@ -0,0 +1,54 @@
+/*
+ * 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 BLE_TGT_STRESS_GATT_H
+#define BLE_TGT_STRESS_GATT_H
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include "host/ble_hs.h"
+#include "host/ble_uuid.h"
+#include "nimble/ble.h"
+#include "modlog/modlog.h"
+#include "misc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern uint16_t hrs_hrm_handle;
+
+/* Heart-rate configuration */
+#define STRESS_GATT_UUID 0xC0DE
+#define STRESS_GATT_READ_UUID 0xC1DE
+#define STRESS_GATT_WRITE_UUID 0xC2DE
+#define STRESS_GATT_INDICATE_UUID 0xC3DE
+#define STRESS_GATT_NOTIFY_UUID 0xC4DE
+
+int gatt_svr_init(void);
+
+void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif //BLE_TGT_STRESS_GATT_H
diff --git a/src/libs/mynewt-nimble/apps/blestress/src/tx_stress.c b/src/libs/mynewt-nimble/apps/blestress/src/tx_stress.c
new file mode 100644
index 00000000..b73adc8a
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/src/tx_stress.c
@@ -0,0 +1,1671 @@
+/*
+ * 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 <host/ble_gap.h>
+#include <console/console.h>
+#include <host/util/util.h>
+#include <host/ble_l2cap.h>
+#include "tx_stress.h"
+
+/* Main test task priority. Set a high value so that the task does not
+ * interfere with event handling */
+#define TX_STRESS_MAIN_TASK_PRIO 0xf0
+#define BASE_UUID_LEN 13
+
+/* Contexts for stress tests. */
+static struct com_stress_test_ctx tx_stress_ctxD = {
+ .conn_handle = 0xffff,
+ .cur_test_id = 0,
+};
+
+static struct com_stress_test_ctx *tx_stress_ctx;
+
+/* Define stack, object and semaphore for test main task. */
+#define TX_STRESS_MAIN_TASK_STACK_SIZE (500)
+static struct os_task tx_stress_main_task;
+static os_stack_t tx_stress_main_task_stack[TX_STRESS_MAIN_TASK_STACK_SIZE];
+static struct os_sem tx_stress_main_sem;
+/* Test use case and address of test advertiser. */
+static int tx_stress_use_case;
+static int completed_tests = 0;
+
+static void
+tx_stress_on_test_finish(int test_num)
+{
+ console_printf("\033[0;32m\nStress test %d completed\033[0m\n", test_num);
+ ++completed_tests;
+ tx_stress_ctx->completed[test_num] = true;
+ os_sem_release(&tx_stress_main_sem);
+}
+
+static void
+tx_stress_simple_scan(ble_gap_event_fn *cb, uint16_t duration)
+{
+ uint8_t own_addr_type;
+ struct ble_gap_ext_disc_params params = {0};
+ int rc;
+
+ /* Figure out address to use while scanning. */
+ rc = ble_hs_id_infer_auto(0, &own_addr_type);
+ if (rc != 0) {
+ console_printf("\033[0;31mError determining own address type; "
+ "rc=%d\033[0m\n", rc);
+ assert(0);
+ }
+
+ params.itvl = BLE_GAP_SCAN_FAST_INTERVAL_MAX;
+ params.passive = 1;
+ params.window = BLE_GAP_SCAN_FAST_WINDOW;
+
+ rc = ble_gap_ext_disc(own_addr_type, duration, 0, 1, 0, 0, &params, NULL,
+ cb, NULL);
+
+ if (rc != 0) {
+ console_printf("\033[0;31mError initiating GAP discovery procedure"
+ "; rc=%d\033[0m\n", rc);
+ }
+}
+
+static int
+tx_stress_simple_connect(ble_gap_event_fn *cb, int test_num)
+{
+ uint8_t own_addr_type;
+ int rc;
+
+ /* Set so any PHY mask allowed. */
+ ble_gap_set_prefered_default_le_phy(TX_PHY_MASK, RX_PHY_MASK);
+
+ /* Figure out address to use while connecting. */
+ rc = ble_hs_id_infer_auto(0, &own_addr_type);
+ if (rc != 0) {
+ MODLOG_DFLT(INFO, "\033[0;31mError determining own address type; "
+ "rc=%d\033[0m\n", rc);
+ return rc;
+ }
+
+ MODLOG_DFLT(INFO, "Connection attempt: %d\n",
+ ++tx_stress_ctx->con_stat[test_num].attempts_num);
+
+ rc = ble_gap_ext_connect(own_addr_type, &tx_stress_ctx->dev_addr,
+ 10000,
+ BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK,
+ NULL, NULL, NULL, cb, NULL);
+
+ if (rc != 0) {
+ MODLOG_DFLT(INFO, "\033[0;31mError during connection; rc=%d\033[0m\n",
+ rc);
+ }
+
+ return rc;
+}
+
+static int
+tx_stress_find_test(struct ble_gap_ext_disc_desc *ext_disc)
+{
+ struct ble_hs_adv_fields fields;
+ int data_len;
+ int rc;
+
+ /* Parser refuses greater length data than 31. But known UUID128 will be
+ * in first 31 bytes of adv data first packet. */
+ if (ext_disc->length_data > 31) {
+ data_len = 31;
+ } else {
+ data_len = ext_disc->length_data;
+ }
+
+ /* Parse part of adv data. */
+ ble_hs_adv_parse_fields(&fields, ext_disc->data, data_len);
+ print_adv_fields(&fields);
+
+ /* UUID128 service data of stress test advert includes only UUID128. */
+ if (fields.svc_data_uuid128_len != 16) {
+ return -1;
+ }
+
+ /* Check if service data include known UUID128. */
+ rc = memcmp(fields.svc_data_uuid128, (uint8_t[]) {0xC0, 0xDE}, 2);
+ if (rc) {
+ return -1;
+ }
+
+ rc = memcmp(fields.svc_data_uuid128 + 3, MYNEWT_VAL(BLE_STRESS_UUID_BASE),
+ BASE_UUID_LEN);
+
+ if (rc != 0) {
+ return -1;
+ }
+
+ /* This UUID 128 byte indicates the stress test ID to be executed. */
+ return fields.svc_data_uuid128[2];
+}
+
+static int
+tx_stress_switcher_gap_event(struct ble_gap_event *event, void *arg)
+{
+ 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) {
+ MODLOG_DFLT(INFO, "Success to connect to device\n");
+ return 0;
+ } else if (event->connect.status == BLE_HS_ETIMEOUT_HCI) {
+ MODLOG_DFLT(INFO, "Connection timeout\n");
+ } else {
+ MODLOG_DFLT(INFO, "Error: connection attempt failed; status=%d\n",
+ event->connect.status);
+ }
+ /* Connect to rx device just to give it a signal to switch test. */
+ tx_stress_simple_connect(tx_stress_switcher_gap_event,
+ tx_stress_ctx->cur_test_id);
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ os_sem_release(&tx_stress_main_sem);
+ return 0;
+
+ case BLE_GAP_EVENT_EXT_DISC:
+ /* Check if caught advert contains known UUID128. The UUID128
+ * contains the ID of test use case to be executed. */
+ rc = tx_stress_find_test(&event->ext_disc);
+ if (rc == 0) {
+ tx_stress_ctx->dev_addr = event->ext_disc.addr;
+ /* Stop scanning. */
+ ble_gap_disc_cancel();
+ /* Add token to semaphore. Main task will start the test. */
+ os_sem_release(&tx_stress_main_sem);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISC_COMPLETE:
+ MODLOG_DFLT(INFO, "Discovery complete; reason=%d\n",
+ event->disc_complete.reason);
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static void
+tx_stress_switch_test()
+{
+ tx_stress_simple_scan(tx_stress_switcher_gap_event, 0);
+ os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER);
+
+ tx_stress_simple_connect(tx_stress_switcher_gap_event, 0);
+}
+
+static int
+tx_stress_1_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed. */
+ MODLOG_DFLT(INFO, "Connection %s; status=%d ",
+ event->connect.status == 0 ? "established" : "failed",
+ event->connect.status);
+
+ if (event->connect.status == 0) {
+ /* Connection successfully established. In this use case
+ * it is error of 'Connect cancel'. Stress test failed. */
+ MODLOG_DFLT(INFO, "Success to connect to device\n");
+ ++tx_stress_ctx->con_stat[1].num;
+
+ ble_gap_terminate(event->connect.conn_handle, BLE_ERR_NO_PAIRING);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static void
+tx_stress_1_test()
+{
+ int rc;
+ uint8_t own_addr_type;
+ ble_addr_t rnd_rx_addr;
+ int delay_time;
+
+ rc = ble_gap_disc_active();
+ assert(rc == 0);
+
+ /* Figure out address to use while advertising. */
+ rc = ble_hs_id_infer_auto(0, &own_addr_type);
+ if (rc != 0) {
+ MODLOG_DFLT(INFO, "\033[0;31mError determining own address type; "
+ "rc=%d\033[0m\n", rc);
+ os_sem_release(&tx_stress_main_sem);
+ return;
+ }
+
+ while (tx_stress_ctx->con_stat[1].attempts_num <
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ /* Rand ble address to connect*/
+ rc = ble_hs_id_gen_rnd(1, &rnd_rx_addr);
+ assert (rc == 0);
+
+ MODLOG_DFLT(INFO, "Connection attempt; num=%d\n",
+ ++tx_stress_ctx->con_stat[1].attempts_num);
+
+ rc = ble_gap_connect(own_addr_type, &rnd_rx_addr, 10000, NULL,
+ tx_stress_1_gap_event, NULL);
+
+ if (rc != 0) {
+ MODLOG_DFLT(INFO, "\033[0;31mConnection error; rc=%d\033[0m\n",
+ rc);
+ os_sem_release(&tx_stress_main_sem);
+ return;
+ }
+
+ MODLOG_DFLT(INFO, "Connect cancel\n");
+ ble_gap_conn_cancel();
+ console_printf("\033[0;32m>\033[0m");
+ }
+
+ console_printf(
+ "\033[0;32m\nFirst part of test completed\nStart second part: "
+ "Connect->random delay->cancel\n\033[0m");
+
+ while (tx_stress_ctx->con_stat[1].attempts_num <
+ 2 * MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ /* Rand ble address to connect*/
+ rc = ble_hs_id_gen_rnd(1, &rnd_rx_addr);
+ assert (rc == 0);
+
+ MODLOG_DFLT(INFO, "Connection attempt; num=%d\n",
+ ++tx_stress_ctx->con_stat[1].attempts_num);
+
+ delay_time = rand() % 1000;
+
+ MODLOG_DFLT(INFO, "Time to delay=%d\n", delay_time);
+
+ rc = ble_gap_connect(own_addr_type, &rnd_rx_addr, 10000, NULL,
+ tx_stress_1_gap_event, NULL);
+
+ if (rc != 0) {
+ MODLOG_DFLT(INFO, "\033[0;31mConnection error; rc=%d\033[0m\n",
+ rc);
+ os_sem_release(&tx_stress_main_sem);
+ return;
+ }
+
+ os_time_delay(os_time_ms_to_ticks32(delay_time));
+
+ MODLOG_DFLT(INFO, "Connect cancel\n");
+ ble_gap_conn_cancel();
+ console_printf("\033[0;32m>\033[0m");
+ }
+
+ tx_stress_on_test_finish(1);
+}
+
+static int
+tx_stress_2_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) {
+ MODLOG_DFLT(INFO, "Success to connect to device\n");
+
+ ++tx_stress_ctx->con_stat[2].num;
+ rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+ assert(rc == 0);
+
+ tx_stress_ctx->conn_handle = desc.conn_handle;
+
+ ble_gap_terminate(event->connect.conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ } else {
+ MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
+ "status=%d\033[0m\n", event->connect.status);
+ os_sem_release(&tx_stress_main_sem);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ console_printf("\033[0;32m>\033[0m");
+
+ if (tx_stress_ctx->con_stat[2].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ tx_stress_on_test_finish(2);
+ return 0;
+ }
+ tx_stress_simple_connect(tx_stress_2_gap_event,
+ tx_stress_ctx->cur_test_id);
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+tx_stress_3_gap_event(struct ble_gap_event *event, void *arg)
+{
+ 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) {
+ ++tx_stress_ctx->con_stat[3].num;
+ MODLOG_DFLT(INFO, "Success to connect to device\n");
+
+ rc = ble_gap_terminate(event->connect.conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+
+ MODLOG_DFLT(INFO, "rc=%d\n", rc);
+ } else {
+ MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
+ "status=%d\033[0m\n", event->connect.status);
+ os_sem_release(&tx_stress_main_sem);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ console_printf("\033[0;32m>\033[0m");
+
+ if (tx_stress_ctx->con_stat[3].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ tx_stress_on_test_finish(3);
+ return 0;
+ }
+ tx_stress_simple_connect(tx_stress_3_gap_event,
+ tx_stress_ctx->cur_test_id);
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+tx_stress_4_con_update(void)
+{
+ int rc;
+
+ /* With every next update at least one param must change. Otherwise no
+ * event occurs and test will not be continued */
+ struct ble_gap_upd_params params = {
+ .itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN,
+ .itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX,
+ .latency = BLE_GAP_INITIAL_CONN_LATENCY,
+ /* So let's change e.g. timeout value. Put ...% 2 ? 1 : 2 to make sure
+ * that value won't grow significantly and will be different with every
+ * iteration. */
+ .supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT +
+ (tx_stress_ctx->con_stat[4].prms_upd_num % 2 ?
+ 1 : 2),
+ .min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN,
+ .max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN,
+ };
+
+ rc = ble_gap_update_params(tx_stress_ctx->conn_handle, &params);
+
+ if (rc == BLE_HS_ENOTCONN) {
+ MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n");
+ assert(0);
+ }
+
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "\033[0;31mError during connection update; "
+ "rc=%d\033[0m\n", rc);
+ assert(0);
+ }
+
+ return rc;
+}
+
+static int
+tx_stress_4_gap_event(struct ble_gap_event *event, void *arg)
+{
+ int rc;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ ++tx_stress_ctx->con_stat[4].attempts_num;
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "Success to connect to device\n");
+
+ ++tx_stress_ctx->con_stat[4].num;
+ tx_stress_ctx->conn_handle = event->connect.conn_handle;
+
+ tx_stress_4_con_update();
+ } else {
+ MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
+ "status=%d\033[0m\n", event->connect.status);
+ os_sem_release(&tx_stress_main_sem);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ tx_stress_on_test_finish(4);
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE:
+ if (event->conn_update.status != 0) {
+ MODLOG_DFLT(INFO, "Connection update failed\n");
+ } else {
+ MODLOG_DFLT(INFO, "Connection updated; num=%d\n",
+ ++tx_stress_ctx->con_stat[4].prms_upd_num);
+ console_printf("\033[0;32m>\033[0m");
+ }
+
+ if (tx_stress_ctx->con_stat[4].prms_upd_num >=
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ ble_gap_terminate(tx_stress_ctx->conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ } else {
+ /* Update connection. */
+ rc = tx_stress_4_con_update();
+
+ if (rc != 0) {
+ MODLOG_DFLT(INFO, "\033[0;31mError: update fail; "
+ "rc=%d\033[0m\n", rc);
+ os_sem_release(&tx_stress_main_sem);
+ }
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE_REQ:
+ MODLOG_DFLT(INFO, "Connection update request\n");
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+tx_stress_5_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "Success to connect to device\n");
+
+ ++tx_stress_ctx->con_stat[5].num;
+ tx_stress_ctx->conn_handle = event->connect.conn_handle;
+ } else {
+ console_printf("\033[0;31mError: Update fail; "
+ "status=%d\033[0m\n", event->connect.status);
+ os_sem_release(&tx_stress_main_sem);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ tx_stress_on_test_finish(5);
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE:
+ if (event->conn_update.status != 0) {
+ MODLOG_DFLT(INFO, "Connection update failed\n");
+ } else {
+ MODLOG_DFLT(INFO, "Connection updated; num=%d\n",
+ ++tx_stress_ctx->con_stat[5].prms_upd_num);
+ console_printf("\033[0;32m>\033[0m");
+ }
+
+ if (tx_stress_ctx->con_stat[5].prms_upd_num >=
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ ble_gap_terminate(tx_stress_ctx->conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE_REQ:
+ MODLOG_DFLT(INFO, "Connection update request\n");
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+tx_stress_6_gap_event(struct ble_gap_event *event, void *arg)
+{
+ static int start_id = 0;
+ int use_case = 0;
+ int adv_pattern_len;
+ const uint8_t *adv_pattern;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_EXT_DISC:
+
+ /* Instance 0 reserved for SWITCH advert. */
+ if (event->ext_disc.sid == 0) {
+ return 0;
+ }
+
+ /* Check if advertiser is known rx device. */
+ if (memcmp(tx_stress_ctx->dev_addr.val,
+ event->ext_disc.addr.val, 6) != 0) {
+ return 0;
+ }
+
+ /* Return -1 if not first package of advert. */
+ use_case = tx_stress_find_test(&event->ext_disc);
+
+ if (use_case > 0) {
+ /* If first package of advert */
+ ++tx_stress_ctx->s6_rcv_adv_first;
+ start_id = 0;
+ adv_pattern = &event->ext_disc.data[29];
+ adv_pattern_len = event->ext_disc.length_data - 29;
+ } else {
+ if (start_id == 0) {
+ return 0;
+ }
+ /* If not first package of advert */
+ adv_pattern = event->ext_disc.data;
+ adv_pattern_len = event->ext_disc.length_data;
+ }
+
+ /* Check data pattern */
+ if (memcmp(adv_pattern, test_6_pattern + start_id,
+ adv_pattern_len) != 0) {
+ /* Pattern does not match. May lost some data or package.
+ * Reset data pattern index. */
+ start_id = 0;
+ return 0;
+ }
+
+ /* At the next adv data package, start comparing from this index.*/
+ start_id += adv_pattern_len;
+
+ /* Check if last packet of advert. */
+ if (event->ext_disc.data_status ==
+ BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE) {
+ /* Got all packets of advert. */
+ ++tx_stress_ctx->s6_rcv_adv_suc;
+ MODLOG_DFLT(INFO, "Got all packets of advert. num=%d\n",
+ tx_stress_ctx->s6_rcv_adv_suc);
+ console_printf("\033[0;32m>\033[0m");
+ start_id = 0;
+
+ if (tx_stress_ctx->s6_rcv_adv_suc >=
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ /* Stop scanning. */
+ ble_gap_disc_cancel();
+ tx_stress_on_test_finish(6);
+ }
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISC_COMPLETE:
+ MODLOG_DFLT(INFO, "Discovery complete; reason=%d\n",
+ event->disc_complete.reason);
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static void
+tx_stress_6_perform(void)
+{
+ tx_stress_simple_scan(tx_stress_6_gap_event, 0);
+ os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER);
+ tx_stress_switch_test();
+}
+
+static int
+tx_stress_7_phy_update(void)
+{
+ int rc;
+ uint8_t tx_phys_mask;
+ uint8_t rx_phys_mask;
+
+ ble_gap_read_le_phy(tx_stress_ctx->conn_handle, &tx_phys_mask,
+ &rx_phys_mask);
+
+
+ /* With every next update at least one param must change */
+ switch (rx_phys_mask) {
+ case BLE_GAP_LE_PHY_1M_MASK:
+ rx_phys_mask = BLE_GAP_LE_PHY_2M_MASK;
+ break;
+ case BLE_GAP_LE_PHY_2M_MASK:
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
+ rx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK;
+ break;
+ case BLE_GAP_LE_PHY_CODED_MASK:
+#endif
+ rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK;
+ break;
+ default:
+ rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK;
+ break;
+ }
+
+ switch (tx_phys_mask) {
+ case BLE_GAP_LE_PHY_1M_MASK:
+ tx_phys_mask = BLE_GAP_LE_PHY_2M_MASK;
+ break;
+ case BLE_GAP_LE_PHY_2M_MASK:
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
+ tx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK;
+ break;
+ case BLE_GAP_LE_PHY_CODED_MASK:
+#endif
+ tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK;
+ break;
+ default:
+ tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK;
+ break;
+ }
+
+ rc = ble_gap_set_prefered_le_phy(tx_stress_ctx->conn_handle,
+ tx_phys_mask, rx_phys_mask, 0);
+
+ if (rc == BLE_HS_ENOTCONN) {
+ MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n");
+ return rc;
+ }
+
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "\033[0;31mError during PHY update; "
+ "rc=%d\033[0m\n", rc);
+ }
+
+ return rc;
+}
+
+static int
+tx_stress_7_gap_event(struct ble_gap_event *event, void *arg)
+{
+ 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) {
+ MODLOG_DFLT(INFO, "Success to connect to device\n");
+
+ ++tx_stress_ctx->con_stat[7].num;
+ tx_stress_ctx->conn_handle = event->connect.conn_handle;
+
+ tx_stress_7_phy_update();
+ } else {
+ console_printf("\033[0;31mError: Update fail; "
+ "status=%d\033[0m\n", event->connect.status);
+ os_sem_release(&tx_stress_main_sem);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ tx_stress_on_test_finish(7);
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE:
+ MODLOG_DFLT(INFO, "Connection updated\n");
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE_REQ:
+ MODLOG_DFLT(INFO, "Connection update request\n");
+ return 0;
+
+ case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
+ if (event->phy_updated.status != 0) {
+ MODLOG_DFLT(INFO, "PHY update failed\n");
+ } else {
+ MODLOG_DFLT(INFO, "PHY updated; num=%d; rx:%d, tx:%d\n",
+ ++tx_stress_ctx->con_stat[7].phy_upd_num,
+ event->phy_updated.rx_phy, event->phy_updated.tx_phy);
+ console_printf("\033[0;32m>\033[0m");
+ }
+
+ if (tx_stress_ctx->con_stat[7].phy_upd_num >=
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ ble_gap_terminate(tx_stress_ctx->conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ } else {
+ /* Update connection. */
+ rc = tx_stress_7_phy_update();
+ if (rc != 0) {
+ console_printf("\033[0;31mError: PHPY update fail; "
+ "rc=%d\033[0m\n", event->phy_updated.status);
+ assert(0);
+ }
+ }
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+tx_stress_8_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "Success to connect to device\n");
+
+ ++tx_stress_ctx->con_stat[8].num;
+ tx_stress_ctx->conn_handle = event->connect.conn_handle;
+ } else {
+ MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
+ "status=%d\033[0m\n", event->connect.status);
+ os_sem_release(&tx_stress_main_sem);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ tx_stress_on_test_finish(8);
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE:
+ if (event->conn_update.status != 0) {
+ MODLOG_DFLT(INFO, "Connection update failed\n");
+ } else {
+ MODLOG_DFLT(INFO, "Connection updated; num=%d\n",
+ ++tx_stress_ctx->con_stat[8].prms_upd_num);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE_REQ:
+ MODLOG_DFLT(INFO, "Connection update request\n");
+ return 0;
+
+ case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
+ if (event->phy_updated.status != 0) {
+ MODLOG_DFLT(INFO, "PHY update failed\n");
+ } else {
+ MODLOG_DFLT(INFO, "PHY updated; num=%d\n",
+ ++tx_stress_ctx->con_stat[8].phy_upd_num);
+ console_printf("\033[0;32m>\033[0m");
+ }
+
+ if (tx_stress_ctx->con_stat[8].phy_upd_num >=
+ MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ ble_gap_terminate(tx_stress_ctx->conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ }
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+tx_stress_9_gap_event(struct ble_gap_event *event, void *arg)
+{
+ ble_addr_t addr;
+ int test;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_EXT_DISC:
+ /* Looking for next instance of test 9 advert. */
+ test = tx_stress_find_test(&event->ext_disc);
+ /* To avoid messing by rest of test 9 events in queue, check if handle
+ * filled */
+ if (test == 9 && tx_stress_ctx->conn_handle == 0xffff) {
+ tx_stress_ctx->conn_handle = 0;
+ ble_gap_disc_cancel();
+ tx_stress_ctx->dev_addr = event->ext_disc.addr;
+ tx_stress_simple_connect(tx_stress_9_gap_event,
+ tx_stress_ctx->cur_test_id);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISC_COMPLETE:
+ if (event->disc_complete.reason == 0 && !tx_stress_ctx->completed[9]) {
+ console_printf("\033[0;31mScanning timeout\033[0m");
+ tx_stress_ctx->completed[9] = true;
+ os_sem_release(&tx_stress_main_sem);
+ return 0;
+ }
+ break;
+
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "Success to connect to device\n");
+ MODLOG_DFLT(INFO, "Connections num: %d\n",
+ ++tx_stress_ctx->con_stat[9].num);
+ console_printf("\033[0;32m>\033[0m");
+ /* Remember max number of handled connections */
+ if (tx_stress_ctx->con_stat[9].num >
+ tx_stress_ctx->con_stat[9].max_num) {
+ tx_stress_ctx->con_stat[9].max_num = tx_stress_ctx->con_stat[9].num;
+ }
+ } else {
+ MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
+ "status=%d\033[0m\n", event->connect.status);
+ }
+ break;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
+ console_printf("\033[0;31mX\033[0m");
+ MODLOG_DFLT(INFO, "Connections num: %d\n",
+ --tx_stress_ctx->con_stat[9].num);
+
+ if (tx_stress_ctx->con_stat[9].num == 0) {
+ os_sem_release(&tx_stress_main_sem);
+ return 0;
+ }
+ break;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+
+ if (tx_stress_ctx->completed[9]) {
+ return 0;
+ }
+
+ /* End use case after specified number of scan times or max config number
+ * of connections */
+ if (tx_stress_ctx->con_stat[9].attempts_num <
+ MYNEWT_VAL(BLE_STRESS_REPEAT) &&
+ tx_stress_ctx->con_stat[9].max_num < MYNEWT_VAL(BLE_MAX_CONNECTIONS)) {
+ if (ble_gap_disc_active() == 0) {
+ /* Generate and set new random address */
+ ble_hs_id_gen_rnd(0, &addr);
+ ble_hs_id_set_rnd(addr.val);
+ tx_stress_ctx->conn_handle = 0xffff;
+ /* Scan for next instance of the test. */
+ tx_stress_simple_scan(tx_stress_9_gap_event, 2000);
+ }
+ } else {
+ tx_stress_ctx->completed[9] = true;
+ os_sem_release(&tx_stress_main_sem);
+ }
+ return 0;
+}
+
+static void
+tx_stress_9_perform()
+{
+ int i, rc;
+
+ /* Scan for next instance of the test. */
+ tx_stress_simple_scan(tx_stress_9_gap_event, 2000);
+
+ os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER);
+
+ /* On use case finishing terminate all handled connections */
+ for (i = 0; i <= MYNEWT_VAL(BLE_MAX_CONNECTIONS); ++i) {
+ rc = ble_gap_conn_find(i, NULL);
+ if (rc == 0) {
+ MODLOG_DFLT(INFO, "Terminating...\n");
+ ble_gap_terminate(i, BLE_ERR_REM_USER_CONN_TERM);
+ }
+ }
+
+ os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER);
+ tx_stress_on_test_finish(9);
+}
+
+static int
+tx_stress_10_l2cap_send(struct ble_l2cap_chan *chan, uint8_t *data,
+ int data_len)
+{
+ struct os_mbuf *data_buf;
+ int rc;
+
+ /* Get mbuf for adv data */
+ data_buf = os_msys_get_pkthdr(data_len, 0);
+ assert(data != NULL);
+
+ /* Fill mbuf with pattern data */
+ rc = os_mbuf_append(data_buf, data, data_len);
+
+ if (rc) {
+ os_mbuf_free_chain(data_buf);
+ assert(0);
+ }
+
+ /* Send data with L2CAP */
+ rc = ble_l2cap_send(chan, data_buf);
+
+ return rc;
+}
+
+void tx_stress_10_timer_ev_cb(struct os_event *ev)
+{
+ assert(ev != NULL);
+
+ if (tx_stress_ctx->rcv_data_flag) {
+ return;
+ }
+
+ tx_stress_ctx->timeout_flag = true;
+ MODLOG_DFLT(INFO, "L2CAP receiving timeout\n");
+ ble_gap_terminate(tx_stress_ctx->conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+}
+
+static void
+tx_stress_10_l2cap_send_req()
+{
+ int rc;
+ /* Send a request to the RX device */
+
+ MODLOG_DFLT(INFO, "L2CAP sending request\n");
+ tx_stress_ctx->timeout_flag = false;
+ tx_stress_ctx->rcv_data_flag = false;
+ stress_start_timer(7000, tx_stress_10_timer_ev_cb);
+
+ /* Get the sending begin time */
+ tx_stress_ctx->begin_us = os_get_uptime_usec();
+
+ /* Send anything just to give a signal to start sending data
+ * by RX device */
+ rc = tx_stress_10_l2cap_send(tx_stress_ctx->chan, (uint8_t *) "S",
+ sizeof("S"));
+ assert(rc == 0);
+}
+
+static int
+tx_stress_10_l2cap_event(struct ble_l2cap_event *event, void *arg)
+{
+ static int i = 0;
+ int64_t us = 0;
+ struct ble_l2cap_chan_info chan_info;
+
+ switch (event->type) {
+ case BLE_L2CAP_EVENT_COC_CONNECTED:
+ /* A new L2CAP connection was established. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "Established L2CAP connection\n");
+ tx_stress_ctx->chan = event->connect.chan;
+
+ ble_l2cap_get_chan_info(event->connect.chan, &chan_info);
+
+ MODLOG_DFLT(INFO,
+ "LE COC connected, conn: %d, chan: 0x%08lx, scid: 0x%04x, "
+ "dcid: 0x%04x, our_mtu: 0x%04x, peer_mtu: 0x%04x\n",
+ event->connect.conn_handle,
+ (uint32_t) event->connect.chan,
+ chan_info.scid,
+ chan_info.dcid,
+ chan_info.our_l2cap_mtu,
+ chan_info.peer_l2cap_mtu);
+
+ tx_stress_10_l2cap_send_req();
+ }
+ return 0;
+
+ case BLE_L2CAP_EVENT_COC_DISCONNECTED:
+ MODLOG_DFLT(INFO, "Remote device disconnected from L2CAP server\n");
+ return 0;
+
+ case BLE_L2CAP_EVENT_COC_ACCEPT:
+ stress_l2cap_coc_accept(event->accept.peer_sdu_size,
+ event->accept.chan);
+ return 0;
+
+ case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
+ /* Get the time of data receive */
+ tx_stress_ctx->end_us = os_get_uptime_usec();
+
+ /* And test after timeout */
+ if (tx_stress_ctx->timeout_flag) {
+ ble_gap_terminate(tx_stress_ctx->conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ return 0;
+ }
+
+ tx_stress_ctx->rcv_data_flag = true;
+
+ stress_l2cap_coc_recv(event->receive.chan, event->receive.sdu_rx);
+
+ /* Time of data sending */
+ us = tx_stress_ctx->end_us - tx_stress_ctx->begin_us;
+ MODLOG_DFLT(INFO, "Time of receiving L2CAP data: %ld \n",
+ tx_stress_ctx->end_us);
+
+ /* Remember size of entire mbuf chain */
+ tx_stress_ctx->rcv_data_bytes = OS_MBUF_PKTLEN(
+ event->receive.sdu_rx);
+ MODLOG_DFLT(INFO, "Num of received bytes: %lld\n",
+ tx_stress_ctx->rcv_data_bytes);
+
+ /* Calculate the bit rate of this send */
+ tx_stress_ctx->s10_bit_rate =
+ stress_calc_bit_rate(us, tx_stress_ctx->rcv_data_bytes);
+ MODLOG_DFLT(INFO, "Bit rate: %d B/s\n", tx_stress_ctx->s10_bit_rate);
+
+ /* Remember the sum of bytes and the time to calculate the average
+ * bit rate. */
+ tx_stress_ctx->bytes_sum += tx_stress_ctx->rcv_data_bytes;
+ tx_stress_ctx->time_sum += us;
+
+ /* Remember max received MTU */
+ if (tx_stress_ctx->s10_max_mtu < tx_stress_ctx->rcv_data_bytes) {
+ tx_stress_ctx->s10_max_mtu = tx_stress_ctx->rcv_data_bytes;
+ }
+ console_printf("\033[0;32m>\033[0m");
+ MODLOG_DFLT(INFO, "Loop nr: %d\n", ++i);
+
+ tx_stress_10_l2cap_send_req();
+ return 0;
+
+ case BLE_L2CAP_EVENT_COC_TX_UNSTALLED:
+ MODLOG_DFLT(INFO, "L2CAP event unstalled\n");
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other L2CAP event occurs; rc=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+tx_stress_10_gap_event(struct ble_gap_event *event, void *arg)
+{
+ int rc;
+ struct os_mbuf *sdu_rx;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "Success to connect to device; num: %d\n",
+ ++tx_stress_ctx->con_stat[10].num);
+
+ sdu_rx = os_msys_get_pkthdr(STRESS_COC_MTU, 0);
+ assert(sdu_rx != NULL);
+
+ tx_stress_ctx->conn_handle = event->connect.conn_handle;
+ rc = ble_l2cap_connect(event->connect.conn_handle, 1,
+ STRESS_COC_MTU, sdu_rx,
+ tx_stress_10_l2cap_event, NULL);
+ assert(rc == 0);
+ } else {
+ MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
+ "status=%d\033[0m\n", event->connect.status);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ tx_stress_ctx->s10_bit_rate = 1000000 * tx_stress_ctx->bytes_sum /
+ tx_stress_ctx->time_sum;
+
+ MODLOG_DFLT(INFO, "Average bit rate: %d B/s\n",
+ tx_stress_ctx->s10_bit_rate);
+ tx_stress_on_test_finish(10);
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+tx_stress_11_gap_event(struct ble_gap_event *event, void *arg)
+{
+ int test;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_EXT_DISC:
+ /* Looking for next instance of test 9 advert. */
+ test = tx_stress_find_test(&event->ext_disc);
+
+ /* To avoid messing by rest of test 9 events in queue, check if handle
+ * filled */
+ if (test == 11 && tx_stress_ctx->conn_handle == 0xffff) {
+ tx_stress_ctx->conn_handle = 0;
+ ble_gap_disc_cancel();
+ tx_stress_ctx->dev_addr = event->ext_disc.addr;
+ tx_stress_simple_connect(tx_stress_11_gap_event,
+ tx_stress_ctx->cur_test_id);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ ++tx_stress_ctx->con_stat[11].num;
+ MODLOG_DFLT(INFO, "Success to connect to device\n");
+ } else {
+ MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
+ "status=%d\033[0m\n", event->connect.status);
+ break;
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ console_printf("\033[0;32m>\033[0m");
+
+ if (tx_stress_ctx->con_stat[11].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ tx_stress_on_test_finish(11);
+ return 0;
+ }
+ break;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+
+ tx_stress_ctx->conn_handle = 0xffff;
+
+ /* Scan for next instance of the test. */
+ tx_stress_simple_scan(tx_stress_11_gap_event, 750);
+
+ return 0;
+}
+
+static int
+tx_stress_12_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ ++tx_stress_ctx->con_stat[12].num;
+ MODLOG_DFLT(INFO, "Success to connect to device\n");
+ tx_stress_ctx->conn_handle = event->connect.conn_handle;
+ } else {
+ MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
+ "status=%d\033[0m\n", event->connect.status);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ /* Finish test after first disconnection */
+ tx_stress_on_test_finish(12);
+ return 0;
+
+ case BLE_GAP_EVENT_NOTIFY_RX:
+ /* Received indication */
+ MODLOG_DFLT(INFO, "Notify RX event\n");
+ console_printf("\033[0;32m>\033[0m");
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+tx_stress_13_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ ++tx_stress_ctx->con_stat[13].num;
+ MODLOG_DFLT(INFO, "Success to connect to device\n");
+ tx_stress_ctx->conn_handle = event->connect.conn_handle;
+ } else {
+ MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
+ "status=%d\033[0m\n", event->connect.status);
+ assert(0);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ /* Finish test after disconnection */
+ tx_stress_on_test_finish(13);
+ return 0;
+
+ case BLE_GAP_EVENT_NOTIFY_RX:
+ MODLOG_DFLT(INFO, "Notify RX event\n");
+ console_printf("\033[0;32m>\033[0m");
+ os_mbuf_free_chain(event->notify_rx.om);
+ ++tx_stress_ctx->rcv_num;
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+tx_stress_14_subs_cb(uint16_t conn_handle, const struct ble_gatt_error *error,
+ struct ble_gatt_attr *attr, void *arg)
+{
+ struct os_mbuf *om;
+ bool *sub;
+ int rc;
+
+ assert(error->status == 0);
+
+ /* If the first subscription after finding cccd */
+ if(arg == NULL) {
+ return 0;
+ }
+
+ sub = (bool*)arg;
+
+ /* Enable notifications */
+ if(*sub == 0) {
+ *sub = true;
+ om = ble_hs_mbuf_from_flat(
+ (uint8_t[]) {0x01, 0x00}, 2);
+
+ tx_stress_ctx->begin_us = tx_stress_ctx->end_us;
+
+ rc = ble_gattc_write(tx_stress_ctx->conn_handle,
+ tx_stress_ctx->dsc_handle, om,
+ tx_stress_14_subs_cb, arg);
+ assert(rc == 0);
+ }
+
+ return 0;
+}
+
+static void
+tx_stress_14_disc_cccd_fn(struct stress_gatt_search_ctx *search_ctx)
+{
+ int rc;
+ struct os_mbuf *om;
+ MODLOG_DFLT(INFO, "CCCD found\n");
+
+ /* Enable notifications */
+ om = ble_hs_mbuf_from_flat((uint8_t[]) {0x01, 0x00}, 2);
+ tx_stress_ctx->begin_us = os_get_uptime_usec();
+ tx_stress_ctx->dsc_handle = search_ctx->dsc_handle;
+
+ rc = ble_gattc_write(tx_stress_ctx->conn_handle,
+ tx_stress_ctx->dsc_handle, om,
+ tx_stress_14_subs_cb, NULL);
+ assert(rc == 0);
+}
+
+static int
+tx_stress_14_gap_event(struct ble_gap_event *event, void *arg)
+{
+ int64_t us = 0;
+ struct os_mbuf *om;
+ int rc;
+ static bool subscribed = true;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ ++tx_stress_ctx->con_stat[14].num;
+ MODLOG_DFLT(INFO, "Success to connect to device\n");
+ tx_stress_ctx->conn_handle = event->connect.conn_handle;
+
+ /* Find CCCD handle (with default UUID16 = 0x2902) */
+ stress_find_dsc_handle(event->connect.conn_handle,
+ BLE_UUID16_DECLARE(STRESS_GATT_UUID),
+ BLE_UUID16_DECLARE(STRESS_GATT_NOTIFY_UUID),
+ BLE_UUID16_DECLARE(0x2902),
+ &tx_stress_14_disc_cccd_fn);
+ } else {
+ MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
+ "status=%d\033[0m\n", event->connect.status);
+ assert(0);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
+ event->disconnect.reason);
+ /* Calc average notifying time */
+ if (tx_stress_ctx->rcv_num > 0) {
+ tx_stress_ctx->s14_notif_time = tx_stress_ctx->time_sum /
+ tx_stress_ctx->rcv_num;
+ }
+ MODLOG_DFLT(INFO, "Average notification time: %d\n",
+ tx_stress_ctx->s14_notif_time);
+ /* Finish test after first disconnection */
+ tx_stress_on_test_finish(14);
+ return 0;
+
+ case BLE_GAP_EVENT_NOTIFY_RX:
+ tx_stress_ctx->end_us = os_get_uptime_usec();
+ MODLOG_DFLT(INFO, "Notify RX event\n");
+
+ /* Time of data sending */
+ us = tx_stress_ctx->end_us - tx_stress_ctx->begin_us;
+ MODLOG_DFLT(INFO, "Notification time: %lld\n us", us);
+
+ tx_stress_ctx->time_sum += us;
+ console_printf("\033[0;32m>\033[0m");
+
+ /* Perform use case specified number of times */
+ if (++tx_stress_ctx->rcv_num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ rc = ble_gap_terminate(event->notify_rx.conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ MODLOG_DFLT(INFO, "rc=%d\n");
+ assert(rc == 0);
+ return 0;
+ }
+
+ /* Disable notifications */
+ subscribed = false;
+ om = ble_hs_mbuf_from_flat(
+ (uint8_t[]) {0x00, 0x00}, 2);
+
+ rc = ble_gattc_write(tx_stress_ctx->conn_handle,
+ tx_stress_ctx->dsc_handle, om,
+ tx_stress_14_subs_cb, &subscribed);
+ assert(rc == 0);
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+tx_stress_15_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error,
+ struct ble_gatt_attr *attr, void *arg)
+{
+ /* Disconnect */
+ ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
+ console_printf("\033[0;32m>\033[0m");
+ return 0;
+}
+
+static void
+tx_stress_15_disc_chr_fn(struct stress_gatt_search_ctx *search_ctx)
+{
+ int rc;
+ struct os_mbuf *om;
+
+ /* Send some data */
+ MODLOG_DFLT(INFO, "Write to chr\n");
+ om = ble_hs_mbuf_from_flat(test_6_pattern, 20);
+
+ rc = ble_gattc_write(tx_stress_ctx->conn_handle,
+ search_ctx->chr_start_handle, om,
+ tx_stress_15_write_cb, NULL);
+ assert(rc == 0);
+}
+
+static int
+tx_stress_15_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed. */
+ if (event->connect.status == 0) {
+ MODLOG_DFLT(INFO, "Success to connect to device; num: %d\n",
+ ++tx_stress_ctx->con_stat[15].num);
+ tx_stress_ctx->conn_handle = event->connect.conn_handle;
+
+ /* Find characteristic handle */
+ stress_find_chr_handle(event->connect.conn_handle,
+ BLE_UUID16_DECLARE(STRESS_GATT_UUID),
+ BLE_UUID16_DECLARE(STRESS_GATT_WRITE_UUID),
+ &tx_stress_15_disc_chr_fn);
+ } else {
+ MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
+ "status=%d\033[0m\n", event->connect.status);
+ assert(0);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ /* Perform use case specified number of times */
+ if(tx_stress_ctx->con_stat[15].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
+ tx_stress_on_test_finish(15);
+ return 0;
+ }
+ /* Reconnect */
+ tx_stress_simple_connect(tx_stress_15_gap_event, 15);
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static int
+scan_for_test_gap_event(struct ble_gap_event *event, void *arg)
+{
+ int use_case;
+ int rc;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_EXT_DISC:
+ /* Check if caught advert contains known UUID128. The UUID128
+ * contains the ID of test use case to be executed. */
+ use_case = tx_stress_find_test(&event->ext_disc);
+ if (use_case > 0) {
+ rc = ble_gap_disc_cancel();
+ tx_stress_ctx->dev_addr = event->ext_disc.addr;
+
+ /* After discovery cancel there are still some events in queue. */
+ if (rc == 0) {
+ tx_stress_use_case = use_case;
+ /* Add token to semaphore. Main task will start the test. */
+ os_sem_release(&tx_stress_main_sem);
+ }
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISC_COMPLETE:
+ /* On timeout */
+ tx_stress_ctx->scan_timeout = true;
+ console_printf("\033[1;36mDiscover complete\033[0m\n");
+ os_sem_release(&tx_stress_main_sem);
+ return 0;
+
+ default:
+ MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
+ return 0;
+ }
+}
+
+static void
+tx_stress_test_perform(int test_num)
+{
+ /* Perform every test only once */
+// if (test_num <= 0 || tx_stress_ctx->completed[test_num] == true) {
+// return;
+// }
+
+ tx_stress_ctx->cur_test_id = test_num;
+ tx_stress_ctx->completed[test_num] = false;
+ tx_stress_ctx->conn_handle = 0xffff;
+
+ console_printf("\033[1;36mStart test num %d - ", test_num);
+
+ /* Start test */
+ switch (test_num) {
+ case 0:
+ return;
+ case 1:
+ console_printf("Stress Connect -> Connect Cancel\033[0m\n");
+ tx_stress_1_test();
+ break;
+ case 2:
+ console_printf("Stress Connect/Disconnect legacy\033[0m\n");
+ tx_stress_simple_connect(&tx_stress_2_gap_event, 2);
+ break;
+ case 3:
+ console_printf("Stress Connect/Disconnect ext adv\033[0m\n");
+ tx_stress_simple_connect(&tx_stress_3_gap_event, 3);
+ break;
+ case 4:
+ console_printf("Stress connection params update (TX)\033[0m\n");
+ tx_stress_simple_connect(&tx_stress_4_gap_event, 4);
+ break;
+ case 5:
+ console_printf("Stress connection params update (RX)\033[0m\n");
+ tx_stress_simple_connect(&tx_stress_5_gap_event, 5);
+ break;
+ case 6:
+ console_printf("Stress Scan\033[0m\n");
+ tx_stress_6_perform();
+ break;
+ case 7:
+ console_printf("Stress PHY Update (TX)\033[0m\n");
+ tx_stress_simple_connect(&tx_stress_7_gap_event, 7);
+ break;
+ case 8:
+ console_printf("Stress PHY Update (RX)\033[0m\n");
+ tx_stress_simple_connect(&tx_stress_8_gap_event, 8);
+ break;
+ case 9:
+ console_printf("Stress multi connection\033[0m\n");
+ tx_stress_9_perform();
+ break;
+ case 10:
+ console_printf("Stress L2CAP send\033[0m\n");
+ tx_stress_simple_connect(&tx_stress_10_gap_event, 10);
+ break;
+ case 11:
+ console_printf("Stress Advertise/Connect/Disconnect\033[0m\n");
+ tx_stress_simple_connect(&tx_stress_11_gap_event, 11);
+ break;
+ case 12:
+ console_printf("Stress GATT indication\033[0m\n");
+ tx_stress_simple_connect(&tx_stress_12_gap_event, 12);
+ break;
+ case 13:
+ console_printf("Stress GATT notification\033[0m\n");
+ tx_stress_simple_connect(&tx_stress_13_gap_event, 13);
+ break;
+ case 14:
+ console_printf("Stress GATT Subscribe/Notify/Unsubscribe\033[0m\n");
+ tx_stress_simple_connect(&tx_stress_14_gap_event, 14);
+ break;
+ case 15:
+ console_printf("Stress Connect/Send/Disconnect\033[0m\n");
+ tx_stress_simple_connect(&tx_stress_15_gap_event, 15);
+ break;
+ default:
+ console_printf("\033[0;31mFound test, but do not know how to perform."
+ "\033[0m\n");
+ assert(0);
+ }
+
+ /* Wait for the test to finish. Then 1 token will be released
+ * allowing to pass through semaphore. */
+ os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER);
+
+ stress_clear_ctx_reusable_var(tx_stress_ctx);
+}
+
+static void
+tx_stress_read_command_cb(void) {
+ console_printf("Start testing\n");
+ os_sem_release(&tx_stress_main_sem);
+}
+
+static void
+tx_stress_main_task_fn(void *arg)
+{
+ int rc;
+
+ tx_stress_ctx = &tx_stress_ctxD;
+
+ console_printf("\033[1;36mTX device\033[0m\n");
+ console_printf("Press ENTER to start: \n");
+ console_init(&tx_stress_read_command_cb);
+
+ /* Waite for pressing ENTER in console */
+ os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER);
+
+ /* Init semaphore with 0 tokens. */
+ rc = os_sem_init(&tx_stress_main_sem, 0);
+ assert(rc == 0);
+
+ /* Start test 1 - Connect/Connect cancel */
+ //tx_stress_test_perform(1);
+
+ while (1) {
+ console_printf("\033[0;36mStart scan for test\033[0m\n");
+
+ /* Scan for known UUID128 of one of the stress tests. */
+ tx_stress_simple_scan(scan_for_test_gap_event, 2000);
+
+ /* Wait for the scan to find the test. Then 1 token will be
+ * released allowing to pass through semaphore. */
+ os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER);
+ if(tx_stress_ctx->scan_timeout) {
+ break;
+ }
+
+ /* Start test. */
+ tx_stress_test_perform(tx_stress_use_case);
+ tx_stress_use_case = -1;
+ }
+
+ /* Print tests results */
+ com_stress_print_report(tx_stress_ctx);
+
+ /* Task should never return */
+ while (1) {
+ /* Delay used only to prevent watchdog to reset the device. */
+ os_time_delay(os_time_ms_to_ticks32(2000));
+ }
+}
+
+void tx_stress_start_auto()
+{
+ /* Start task that will run all stress tests one by one. */
+ os_task_init(&tx_stress_main_task, "tx_stress_main_task",
+ tx_stress_main_task_fn, NULL, TX_STRESS_MAIN_TASK_PRIO,
+ OS_WAIT_FOREVER, tx_stress_main_task_stack,
+ TX_STRESS_MAIN_TASK_STACK_SIZE);
+}
diff --git a/src/libs/mynewt-nimble/apps/blestress/src/tx_stress.h b/src/libs/mynewt-nimble/apps/blestress/src/tx_stress.h
new file mode 100644
index 00000000..83ed3020
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/src/tx_stress.h
@@ -0,0 +1,49 @@
+/*
+ * 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 _BLE_STRESS_TX_H
+#define _BLE_STRESS_TX_H
+
+#include <assert.h>
+#include <string.h>
+#include <host/ble_gap.h>
+#include <stdlib.h>
+
+/* BLE */
+#include "nimble/ble.h"
+#include "host/ble_hs.h"
+
+#include "misc.h"
+#include "stress.h"
+#include "stress_gatt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Scan and execute tests one by one.
+ */
+void tx_stress_start_auto();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_BLE_STRESS_TX_H
diff --git a/src/libs/mynewt-nimble/apps/blestress/syscfg.yml b/src/libs/mynewt-nimble/apps/blestress/syscfg.yml
new file mode 100644
index 00000000..344466ce
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/blestress/syscfg.yml
@@ -0,0 +1,74 @@
+#
+# 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.
+#
+
+# Settings this app defines.
+syscfg.defs:
+ BLE_STRESS_TEST_ROLE:
+ description: 0 - TX device, 1 - RX device
+ value: 0
+
+ BLE_STRESS_REPEAT:
+ description: Number of times to repeat each stress test use case
+ value: 50
+
+ BLE_STRESS_UUID_BASE:
+ description: Part of the test UUID that is specific to the current
+ device couple.
+ value: ((uint8_t[13]){0xA5, 0x4A, 0xB4, 0x44, 0xC3, 0xBF, 0xB5,
+ 0xF8, 0xF9, 0x42, 0x83, 0xA1, 0xDA})
+
+# Settings this app overrides.
+syscfg.vals:
+ # Change these settings:
+
+ # Set 0 to print all debug logs, but keep in mind that plenty of
+ # adv packets will be lost during scan tests.
+ LOG_LEVEL: 1
+
+ # The maximum number of concurrent connections. For some devices, this
+ # value will need to be reduced due to the RAM capacity.
+ BLE_MAX_CONNECTIONS: 50
+
+ # Should not change these settings:
+
+ # Enable Extended Advertising
+ BLE_EXT_ADV: 1
+
+ # Max advertising data size
+ BLE_EXT_ADV_MAX_SIZE: 1650
+
+ # Number of multi-advertising instances. Note that due
+ # to historical reasons total number of advertising
+ # instances is BLE_MULTI_ADV_INSTANCES + 1 as instance
+ # 0 is always available
+ BLE_MULTI_ADV_INSTANCES: 2
+
+ # Controller uses msys pool for storing advertising data and scan responses.
+ # Since we advertise a lot of data (~4k in total) at the same time we need
+ # to increase block count.
+ MSYS_1_BLOCK_COUNT: 50
+
+ #
+ BLE_L2CAP_COC_MAX_NUM: 2
+
+ # Enable 2M PHY
+ BLE_LL_CFG_FEAT_LE_2M_PHY: 1
+
+ # Enable CODED PHY
+ BLE_LL_CFG_FEAT_LE_CODED_PHY: 1
diff --git a/src/libs/mynewt-nimble/apps/btshell/pkg.yml b/src/libs/mynewt-nimble/apps/btshell/pkg.yml
new file mode 100644
index 00000000..29541b74
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/btshell/pkg.yml
@@ -0,0 +1,39 @@
+# 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/btshell
+pkg.type: app
+pkg.description: Shell application exposing the nimble GAP and GATT.
+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/log/full"
+ - "@apache-mynewt-core/sys/log/modlog"
+ - "@apache-mynewt-core/sys/stats/full"
+ - "@apache-mynewt-core/sys/console/full"
+ - "@apache-mynewt-core/sys/shell"
+ - nimble/host
+ - nimble/host/services/gap
+ - nimble/host/services/gatt
+ - nimble/host/store/ram
+ - nimble/transport
+
+pkg.deps.BTSHELL_ANS:
+ - nimble/host/services/ans
diff --git a/src/libs/mynewt-nimble/apps/btshell/src/btshell.h b/src/libs/mynewt-nimble/apps/btshell/src/btshell.h
new file mode 100644
index 00000000..7c978221
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/btshell/src/btshell.h
@@ -0,0 +1,212 @@
+/*
+ * 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_BTSHELL_PRIV_
+#define H_BTSHELL_PRIV_
+
+#include <inttypes.h>
+#include "os/mynewt.h"
+#include "nimble/ble.h"
+#include "nimble/nimble_opt.h"
+#include "modlog/modlog.h"
+
+#include "host/ble_gatt.h"
+#include "host/ble_gap.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ble_gap_white_entry;
+struct ble_hs_adv_fields;
+struct ble_gap_upd_params;
+struct ble_gap_conn_params;
+struct hci_adv_params;
+struct ble_l2cap_sig_update_req;
+struct ble_l2cap_sig_update_params;
+union ble_store_value;
+union ble_store_key;
+struct ble_gap_adv_params;
+struct ble_gap_conn_desc;
+struct ble_gap_disc_params;
+
+struct btshell_dsc {
+ SLIST_ENTRY(btshell_dsc) next;
+ struct ble_gatt_dsc dsc;
+};
+SLIST_HEAD(btshell_dsc_list, btshell_dsc);
+
+struct btshell_chr {
+ SLIST_ENTRY(btshell_chr) next;
+ struct ble_gatt_chr chr;
+
+ struct btshell_dsc_list dscs;
+};
+SLIST_HEAD(btshell_chr_list, btshell_chr);
+
+struct btshell_svc {
+ SLIST_ENTRY(btshell_svc) next;
+ struct ble_gatt_svc svc;
+ struct btshell_chr_list chrs;
+ bool discovered;
+};
+
+SLIST_HEAD(btshell_svc_list, btshell_svc);
+
+struct btshell_l2cap_coc {
+ SLIST_ENTRY(btshell_l2cap_coc) next;
+ struct ble_l2cap_chan *chan;
+ bool stalled;
+};
+
+SLIST_HEAD(btshell_l2cap_coc_list, btshell_l2cap_coc);
+
+struct btshell_conn {
+ uint16_t handle;
+ struct btshell_svc_list svcs;
+ struct btshell_l2cap_coc_list coc_list;
+};
+
+struct btshell_scan_opts {
+ uint16_t limit;
+ uint8_t ignore_legacy:1;
+ uint8_t periodic_only:1;
+};
+
+extern struct btshell_conn btshell_conns[MYNEWT_VAL(BLE_MAX_CONNECTIONS)];
+extern int btshell_num_conns;
+
+int btshell_exchange_mtu(uint16_t conn_handle);
+int btshell_disc_svcs(uint16_t conn_handle);
+int btshell_disc_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid);
+int btshell_disc_all_chrs(uint16_t conn_handle, uint16_t start_handle,
+ uint16_t end_handle);
+int btshell_disc_all_chrs_in_svc(uint16_t conn_handle, struct btshell_svc *svc);
+int btshell_disc_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle,
+ uint16_t end_handle, const ble_uuid_t *uuid);
+int btshell_disc_all_dscs(uint16_t conn_handle, uint16_t start_handle,
+ uint16_t end_handle);
+int btshell_disc_full(uint16_t conn_handle);
+int btshell_find_inc_svcs(uint16_t conn_handle, uint16_t start_handle,
+ uint16_t end_handle);
+int btshell_read(uint16_t conn_handle, uint16_t attr_handle);
+int btshell_read_long(uint16_t conn_handle, uint16_t attr_handle,
+ uint16_t offset);
+int btshell_read_by_uuid(uint16_t conn_handle, uint16_t start_handle,
+ uint16_t end_handle, const ble_uuid_t *uuid);
+int btshell_read_mult(uint16_t conn_handle, uint16_t *attr_handles,
+ int num_attr_handles);
+int btshell_write(uint16_t conn_handle, uint16_t attr_handle,
+ struct os_mbuf *om);
+int btshell_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle,
+ struct os_mbuf *om);
+int btshell_write_long(uint16_t conn_handle, uint16_t attr_handle,
+ uint16_t offset, struct os_mbuf *om);
+int btshell_write_reliable(uint16_t conn_handle,
+ struct ble_gatt_attr *attrs, int num_attrs);
+#if MYNEWT_VAL(BLE_EXT_ADV)
+int btshell_ext_adv_configure(uint8_t instance,
+ const struct ble_gap_ext_adv_params *params,
+ int8_t *selected_tx_power);
+int btshell_ext_adv_start(uint8_t instance, int duration,
+ int max_events, bool restart);
+int btshell_ext_adv_stop(uint8_t instance);
+#endif
+int btshell_adv_start(uint8_t own_addr_type, const ble_addr_t *direct_addr,
+ int32_t duration_ms,
+ const struct ble_gap_adv_params *params,
+ bool restart);
+int btshell_adv_stop(void);
+int btshell_conn_initiate(uint8_t own_addr_type, const ble_addr_t *peer_addr,
+ int32_t duration_ms,
+ struct ble_gap_conn_params *params);
+int btshell_ext_conn_initiate(uint8_t own_addr_type,
+ const ble_addr_t *peer_addr,
+ int32_t duration_ms,
+ struct ble_gap_conn_params *phy_1m_params,
+ struct ble_gap_conn_params *phy_2m_params,
+ struct ble_gap_conn_params *phy_coded_params);
+int btshell_conn_cancel(void);
+int btshell_term_conn(uint16_t conn_handle, uint8_t reason);
+int btshell_wl_set(ble_addr_t *addrs, int addrs_count);
+int btshell_scan(uint8_t own_addr_type, int32_t duration_ms,
+ const struct ble_gap_disc_params *disc_params, void *cb_args);
+int btshell_ext_scan(uint8_t own_addr_type, uint16_t duration, uint16_t period,
+ uint8_t filter_duplicates, uint8_t filter_policy,
+ uint8_t limited,
+ const struct ble_gap_ext_disc_params *uncoded_params,
+ const struct ble_gap_ext_disc_params *coded_params,
+ void *cb_args);
+int btshell_scan_cancel(void);
+int btshell_update_conn(uint16_t conn_handle,
+ struct ble_gap_upd_params *params);
+void btshell_notify(uint16_t attr_handle);
+int btshell_datalen(uint16_t conn_handle, uint16_t tx_octets,
+ uint16_t tx_time);
+int btshell_l2cap_update(uint16_t conn_handle,
+ struct ble_l2cap_sig_update_params *params);
+int btshell_sec_start(uint16_t conn_handle);
+int btshell_sec_pair(uint16_t conn_handle);
+int btshell_sec_unpair(ble_addr_t *peer_addr);
+int btshell_sec_restart(uint16_t conn_handle, uint8_t key_size,
+ uint8_t *ltk, uint16_t ediv,
+ uint64_t rand_val, int auth);
+int btshell_tx_start(uint16_t conn_handle, uint16_t len, uint16_t rate,
+ uint16_t num);
+void btshell_tx_stop(void);
+int btshell_rssi(uint16_t conn_handle, int8_t *out_rssi);
+int btshell_l2cap_create_srv(uint16_t psm, uint16_t mtu, int accept_response);
+int btshell_l2cap_connect(uint16_t conn, uint16_t psm, uint16_t mtu, uint8_t num);
+int btshell_l2cap_disconnect(uint16_t conn, uint16_t idx);
+int btshell_l2cap_send(uint16_t conn, uint16_t idx, uint16_t bytes);
+int btshell_l2cap_reconfig(uint16_t conn_handle, uint16_t mtu,
+ uint8_t num, uint8_t idxs[]);
+
+int btshell_gap_event(struct ble_gap_event *event, void *arg);
+void btshell_sync_stats(uint16_t handle);
+
+/** 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);
+void gatt_svr_print_svcs(void);
+
+/** Misc. */
+void print_bytes(const uint8_t *bytes, int len);
+void print_mbuf(const struct os_mbuf *om);
+void print_addr(const void *addr);
+void print_uuid(const ble_uuid_t *uuid);
+int svc_is_empty(const struct btshell_svc *svc);
+uint16_t chr_end_handle(const struct btshell_svc *svc,
+ const struct btshell_chr *chr);
+int chr_is_empty(const struct btshell_svc *svc, const struct btshell_chr *chr);
+void print_conn_desc(const struct ble_gap_conn_desc *desc);
+void print_svc(struct btshell_svc *svc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/btshell/src/cmd.c b/src/libs/mynewt-nimble/apps/btshell/src/cmd.c
new file mode 100644
index 00000000..8a878756
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/btshell/src/cmd.c
@@ -0,0 +1,4659 @@
+/*
+ * 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 <inttypes.h>
+#include <errno.h>
+#include <string.h>
+#include "os/mynewt.h"
+#include "bsp/bsp.h"
+
+#include "nimble/ble.h"
+#include "nimble/nimble_opt.h"
+#include "nimble/hci_common.h"
+#include "host/ble_gap.h"
+#include "host/ble_hs_adv.h"
+#include "host/ble_sm.h"
+#include "host/ble_eddystone.h"
+#include "host/ble_hs_id.h"
+#include "services/gatt/ble_svc_gatt.h"
+#include "../src/ble_hs_priv.h"
+
+#include "console/console.h"
+#include "shell/shell.h"
+
+#include "cmd.h"
+#include "btshell.h"
+#include "cmd_gatt.h"
+#include "cmd_l2cap.h"
+
+#define BTSHELL_MODULE "btshell"
+
+int
+cmd_parse_conn_start_end(uint16_t *out_conn, uint16_t *out_start,
+ uint16_t *out_end)
+{
+ int rc;
+
+ *out_conn = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ return rc;
+ }
+
+ *out_start = parse_arg_uint16("start", &rc);
+ if (rc != 0) {
+ return rc;
+ }
+
+ *out_end = parse_arg_uint16("end", &rc);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+static const struct kv_pair cmd_own_addr_types[] = {
+ { "public", BLE_OWN_ADDR_PUBLIC },
+ { "random", BLE_OWN_ADDR_RANDOM },
+ { "rpa_pub", BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT },
+ { "rpa_rnd", BLE_OWN_ADDR_RPA_RANDOM_DEFAULT },
+ { NULL }
+};
+
+static const struct kv_pair cmd_peer_addr_types[] = {
+ { "public", BLE_ADDR_PUBLIC },
+ { "random", BLE_ADDR_RANDOM },
+ { "public_id", BLE_ADDR_PUBLIC_ID },
+ { "random_id", BLE_ADDR_RANDOM_ID },
+ { NULL }
+};
+
+static const struct kv_pair cmd_addr_type[] = {
+ { "public", BLE_ADDR_PUBLIC },
+ { "random", BLE_ADDR_RANDOM },
+ { NULL }
+};
+
+
+static int
+parse_dev_addr(const char *prefix, const struct kv_pair *addr_types,
+ ble_addr_t *addr)
+{
+ char name[32];
+ int rc;
+
+ /* XXX string operations below are not quite safe, but do we care? */
+
+ if (!prefix) {
+ name[0] = '\0';
+ } else {
+ strcpy(name, prefix);
+ }
+
+ strcat(name, "addr");
+ rc = parse_arg_addr(name, addr);
+ if (rc == ENOENT) {
+ /* not found */
+ return rc;
+ } else if (rc == EAGAIN) {
+ /* address found, but no type provided */
+ strcat(name, "_type");
+ addr->type = parse_arg_kv(name, addr_types, &rc);
+ if (rc == ENOENT) {
+ addr->type = BLE_ADDR_PUBLIC;
+ } else if (rc != 0) {
+ return rc;
+ }
+ } else if (rc != 0) {
+ /* error parsing address */
+ return rc;
+ } else {
+ /* full address found, but let's just make sure there is no type arg */
+ strcat(name, "_type");
+ if (parse_arg_extract(name)) {
+ return E2BIG;
+ }
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+ * $advertise *
+ *****************************************************************************/
+static const struct kv_pair cmd_adv_filt_types[] = {
+ { "none", BLE_HCI_ADV_FILT_NONE },
+ { "scan", BLE_HCI_ADV_FILT_SCAN },
+ { "conn", BLE_HCI_ADV_FILT_CONN },
+ { "both", BLE_HCI_ADV_FILT_BOTH },
+ { NULL }
+};
+
+#if MYNEWT_VAL(BLE_EXT_ADV)
+static struct kv_pair cmd_ext_adv_phy_opts[] = {
+ { "1M", 0x01 },
+ { "2M", 0x02 },
+ { "coded", 0x03 },
+ { NULL }
+};
+
+static int
+cmd_advertise_configure(int argc, char **argv)
+{
+ struct ble_gap_ext_adv_params params = {0};
+ int8_t selected_tx_power;
+ uint8_t instance;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ instance = parse_arg_uint8_dflt("instance", 0, &rc);
+ if (rc != 0 || instance >= BLE_ADV_INSTANCES) {
+ console_printf("invalid instance\n");
+ return rc;
+ }
+
+ memset(&params, 0, sizeof(params));
+
+ params.legacy_pdu = parse_arg_bool_dflt("legacy", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'legacy' parameter\n");
+ return rc;
+ }
+
+ if (params.legacy_pdu) {
+ params.connectable = 1;
+ params.scannable = 1;
+ }
+
+ params.connectable = parse_arg_bool_dflt("connectable", params.connectable, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'connectable' parameter\n");
+ return rc;
+ }
+
+ params.scannable = parse_arg_bool_dflt("scannable", params.scannable, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'scannable' parameter\n");
+ return rc;
+ }
+
+ params.high_duty_directed = parse_arg_bool_dflt("high_duty", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'high_duty' parameter\n");
+ return rc;
+ }
+
+ params.anonymous = parse_arg_bool_dflt("anonymous", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'anonymous' parameter\n");
+ return rc;
+ }
+
+ params.include_tx_power = parse_arg_bool_dflt("include_tx_power", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'include_tx_power' parameter\n");
+ return rc;
+ }
+
+ params.scan_req_notif = parse_arg_bool_dflt("scan_req_notif", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'scan_req_notif' parameter\n");
+ return rc;
+ }
+
+ rc = parse_dev_addr("peer_", cmd_peer_addr_types, &params.peer);
+ if (rc == 0) {
+ params.directed = 1;
+ } else if (rc == ENOENT) {
+ /* skip, no peer address provided */
+ } else {
+ console_printf("invalid 'peer_addr' parameter\n");
+ return rc;
+ }
+
+
+ params.directed = parse_arg_bool_dflt("directed", params.directed, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'directed' parameter\n");
+ return rc;
+ }
+
+ if (params.directed && params.legacy_pdu) {
+ params.scannable = 0;
+ }
+
+ params.own_addr_type = parse_arg_kv_dflt("own_addr_type",
+ cmd_own_addr_types,
+ BLE_OWN_ADDR_PUBLIC, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'own_addr_type' parameter\n");
+ return rc;
+ }
+
+ params.channel_map = parse_arg_uint8_dflt("channel_map", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'channel_map' parameter\n");
+ return rc;
+ }
+
+ params.filter_policy = parse_arg_kv_dflt("filter", cmd_adv_filt_types,
+ BLE_HCI_ADV_FILT_NONE, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'filter' parameter\n");
+ return rc;
+ }
+
+ params.itvl_min = parse_arg_time_dflt("interval_min", 625, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'interval_min' parameter\n");
+ return rc;
+ }
+
+ params.itvl_max = parse_arg_time_dflt("interval_max", 625, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'interval_max' parameter\n");
+ return rc;
+ }
+
+ params.tx_power = parse_arg_long_bounds_dflt("tx_power",
+ -127, 127, 127, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'tx_power' parameter\n");
+ return rc;
+ }
+
+ params.primary_phy = parse_arg_kv_dflt("primary_phy", cmd_ext_adv_phy_opts,
+ 1, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'primary_phy' parameter\n");
+ return rc;
+ }
+
+ params.secondary_phy = parse_arg_kv_dflt("secondary_phy",
+ cmd_ext_adv_phy_opts,
+ params.primary_phy, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'secondary_phy' parameter\n");
+ return rc;
+ }
+
+ params.sid = parse_arg_uint8_dflt("sid", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'sid' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_ext_adv_configure(instance, &params, &selected_tx_power);
+ if (rc) {
+ console_printf("failed to configure advertising instance\n");
+ return rc;
+ }
+
+ console_printf("Instance %u configured (selected tx power: %d)\n",
+ instance, selected_tx_power);
+
+ return 0;
+}
+
+static int
+cmd_advertise_set_addr(int argc, char **argv)
+{
+ ble_addr_t addr;
+ uint8_t instance;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ instance = parse_arg_uint8_dflt("instance", 0, &rc);
+ if (rc != 0 || instance >= BLE_ADV_INSTANCES) {
+ console_printf("invalid instance\n");
+ return rc;
+ }
+
+ rc = parse_arg_mac("addr", addr.val);
+ if (rc != 0) {
+ console_printf("invalid 'addr' parameter\n");
+ return rc;
+ }
+
+ addr.type = BLE_ADDR_RANDOM;
+
+ rc = ble_gap_ext_adv_set_addr(instance, &addr);
+ if (rc) {
+ console_printf("failed to start advertising instance\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+cmd_advertise_start(int argc, char **argv)
+{
+ int max_events;
+ uint8_t instance;
+ int duration;
+ bool restart;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ instance = parse_arg_uint8_dflt("instance", 0, &rc);
+ if (rc != 0 || instance >= BLE_ADV_INSTANCES) {
+ console_printf("invalid instance\n");
+ return rc;
+ }
+
+ duration = parse_arg_uint16_dflt("duration", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'duration' parameter\n");
+ return rc;
+ }
+
+ max_events = parse_arg_uint8_dflt("max_events", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'max_events' parameter\n");
+ return rc;
+ }
+
+ restart = parse_arg_bool_dflt("restart", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'restart' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_ext_adv_start(instance, duration, max_events, restart);
+ if (rc) {
+ console_printf("failed to start advertising instance\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+cmd_advertise_stop(int argc, char **argv)
+{
+ uint8_t instance;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ instance = parse_arg_uint8_dflt("instance", 0, &rc);
+ if (rc != 0 || instance >= BLE_ADV_INSTANCES) {
+ console_printf("invalid instance\n");
+ return rc;
+ }
+
+ rc = btshell_ext_adv_stop(instance);
+ if (rc) {
+ console_printf("failed to stop advertising instance\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+cmd_advertise_remove(int argc, char **argv)
+{
+ uint8_t instance;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ instance = parse_arg_uint8_dflt("instance", 0, &rc);
+ if (rc != 0 || instance >= BLE_ADV_INSTANCES) {
+ console_printf("invalid instance\n");
+ return rc;
+ }
+
+ rc = ble_gap_ext_adv_remove(instance);
+ if (rc) {
+ console_printf("failed to remove advertising instance\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param advertise_configure_params[] = {
+ {"instance", "default: 0"},
+ {"connectable", "connectable advertising, usage: =[0-1], default: 0"},
+ {"scannable", "scannable advertising, usage: =[0-1], default: 0"},
+ {"directed", "directed advertising, usage: =[0-1], default: 0"},
+ {"peer_addr_type", "usage: =[public|random|public_id|random_id], default: public"},
+ {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"},
+ {"own_addr_type", "usage: =[public|random|rpa_pub|rpa_rnd], default: public"},
+ {"channel_map", "usage: =[0x00-0xff], default: 0"},
+ {"filter", "usage: =[none|scan|conn|both], default: none"},
+ {"interval_min", "usage: =[0-UINT32_MAX], default: 0"},
+ {"interval_max", "usage: =[0-UINT32_MAX], default: 0"},
+ {"tx_power", "usage: =[-127-127], default: 127"},
+ {"primary_phy", "usage: =[1M|coded], default: 1M"},
+ {"secondary_phy", "usage: =[1M|2M|coded], default: primary_phy"},
+ {"sid", "usage: =[0-UINT8_MAX], default: 0"},
+ {"high_duty", "usage: =[0-1], default: 0"},
+ {"anonymous", "enable anonymous advertising, usage: =[0-1], default: 0"},
+ {"legacy", "use legacy PDUs, usage: =[0-1], default: 0"},
+ {"include_tx_power", "include TX power in PDU, usage: =[0-1], default: 0"},
+ {"scan_req_notif", "enable Scan Request notification usage: =[0-1], default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help advertise_configure_help = {
+ .summary = "configure new advertising instance",
+ .usage = NULL,
+ .params = advertise_configure_params,
+};
+
+static const struct shell_param advertise_set_addr_params[] = {
+ {"instance", "default: 0"},
+ {"addr", "usage: =[XX:XX:XX:XX:XX:XX]"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help advertise_set_addr_help = {
+ .summary = "set advertising instance random address",
+ .usage = NULL,
+ .params = advertise_set_addr_params,
+};
+
+static const struct shell_param advertise_start_params[] = {
+ {"instance", "default: 0"},
+ {"duration", "advertising duration in 10ms units, default: 0 (forever)"},
+ {"max_events", "max number of advertising events, default: 0 (no limit)"},
+ {"restart", "restart advertising after disconnect, usage: =[0-1], default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help advertise_start_help = {
+ .summary = "start advertising instance",
+ .usage = NULL,
+ .params = advertise_start_params,
+};
+
+static const struct shell_param advertise_stop_params[] = {
+ {"instance", "default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help advertise_stop_help = {
+ .summary = "stop advertising instance",
+ .usage = NULL,
+ .params = advertise_stop_params,
+};
+
+static const struct shell_param advertise_remove_params[] = {
+ {"instance", "default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help advertise_remove_help = {
+ .summary = "remove advertising instance",
+ .usage = NULL,
+ .params = advertise_remove_params,
+};
+#endif
+
+#else
+static const struct kv_pair cmd_adv_conn_modes[] = {
+ { "non", BLE_GAP_CONN_MODE_NON },
+ { "und", BLE_GAP_CONN_MODE_UND },
+ { "dir", BLE_GAP_CONN_MODE_DIR },
+ { NULL }
+};
+
+static const struct kv_pair cmd_adv_disc_modes[] = {
+ { "non", BLE_GAP_DISC_MODE_NON },
+ { "ltd", BLE_GAP_DISC_MODE_LTD },
+ { "gen", BLE_GAP_DISC_MODE_GEN },
+ { NULL }
+};
+
+static int
+cmd_advertise(int argc, char **argv)
+{
+ struct ble_gap_adv_params params;
+ int32_t duration_ms;
+ ble_addr_t peer_addr;
+ ble_addr_t *peer_addr_param = &peer_addr;
+ uint8_t own_addr_type;
+ bool restart;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (argc > 1 && strcmp(argv[1], "stop") == 0) {
+ rc = btshell_adv_stop();
+ if (rc != 0) {
+ console_printf("advertise stop fail: %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+ }
+
+ params.conn_mode = parse_arg_kv_dflt("conn", cmd_adv_conn_modes,
+ BLE_GAP_CONN_MODE_UND, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ params.disc_mode = parse_arg_kv_dflt("discov", cmd_adv_disc_modes,
+ BLE_GAP_DISC_MODE_GEN, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'discov' parameter\n");
+ return rc;
+ }
+
+ rc = parse_dev_addr("peer_", cmd_peer_addr_types, &peer_addr);
+ if (rc == ENOENT) {
+ peer_addr_param = NULL;
+ } else if (rc != 0) {
+ console_printf("invalid 'peer_addr' parameter\n");
+ return rc;
+ }
+
+ restart = parse_arg_bool_dflt("restart", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'restart' parameter\n");
+ return rc;
+ }
+
+ own_addr_type = parse_arg_kv_dflt("own_addr_type", cmd_own_addr_types,
+ BLE_OWN_ADDR_PUBLIC, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'own_addr_type' parameter\n");
+ return rc;
+ }
+
+ params.channel_map = parse_arg_uint8_dflt("channel_map", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'channel_map' parameter\n");
+ return rc;
+ }
+
+ params.filter_policy = parse_arg_kv_dflt("filter", cmd_adv_filt_types,
+ BLE_HCI_ADV_FILT_NONE, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'filter' parameter\n");
+ return rc;
+ }
+
+ params.itvl_min = parse_arg_time_dflt("interval_min", 625, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'interval_min' parameter\n");
+ return rc;
+ }
+
+ params.itvl_max = parse_arg_time_dflt("interval_max", 625, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'interval_max' parameter\n");
+ return rc;
+ }
+
+ params.high_duty_cycle = parse_arg_bool_dflt("high_duty", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'high_duty' parameter\n");
+ return rc;
+ }
+
+ duration_ms = parse_arg_long_bounds_dflt("duration", 1, INT32_MAX,
+ BLE_HS_FOREVER, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'duration' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_adv_start(own_addr_type, peer_addr_param, duration_ms,
+ &params, restart);
+ if (rc != 0) {
+ console_printf("advertise fail: %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param advertise_params[] = {
+ {"stop", "stop advertising procedure"},
+ {"conn", "connectable mode, usage: =[non|und|dir], default: und"},
+ {"discov", "discoverable mode, usage: =[non|ltd|gen], default: gen"},
+ {"peer_addr_type", "usage: =[public|random|public_id|random_id], default: public"},
+ {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"},
+ {"own_addr_type", "usage: =[public|random|rpa_pub|rpa_rnd], default: public"},
+ {"channel_map", "usage: =[0x00-0xff], default: 0"},
+ {"filter", "usage: =[none|scan|conn|both], default: none"},
+ {"interval_min", "usage: =[0-UINT16_MAX], default: 0"},
+ {"interval_max", "usage: =[0-UINT16_MAX], default: 0"},
+ {"high_duty", "usage: =[0-1], default: 0"},
+ {"duration", "usage: =[1-INT32_MAX], default: INT32_MAX"},
+ {"restart", "restart advertising after disconnect, usage: =[0-1], default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help advertise_help = {
+ .summary = "start/stop advertising with specific parameters",
+ .usage = NULL,
+ .params = advertise_params,
+};
+#endif
+#endif
+
+/*****************************************************************************
+ * $connect *
+ *****************************************************************************/
+
+static struct kv_pair cmd_ext_conn_phy_opts[] = {
+ { "none", 0x00 },
+ { "1M", 0x01 },
+ { "coded", 0x02 },
+ { "both", 0x03 },
+ { "all", 0x04 },
+ { NULL }
+};
+
+static int
+cmd_connect(int argc, char **argv)
+{
+ struct ble_gap_conn_params phy_1M_params = {0};
+ struct ble_gap_conn_params phy_coded_params = {0};
+ struct ble_gap_conn_params phy_2M_params = {0};
+ uint8_t ext;
+ int32_t duration_ms;
+ ble_addr_t peer_addr;
+ ble_addr_t *peer_addr_param = &peer_addr;
+ int own_addr_type;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (argc > 1 && strcmp(argv[1], "cancel") == 0) {
+ rc = btshell_conn_cancel();
+ if (rc != 0) {
+ console_printf("connection cancel fail: %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+ }
+
+ ext = parse_arg_kv_dflt("extended", cmd_ext_conn_phy_opts, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'extended' parameter\n");
+ return rc;
+ }
+
+ rc = parse_dev_addr("peer_", cmd_peer_addr_types, &peer_addr);
+ if (rc == ENOENT) {
+ /* With no "peer_addr" specified we'll use white list */
+ peer_addr_param = NULL;
+ } else if (rc != 0) {
+ console_printf("invalid 'peer_addr' parameter\n");
+ return rc;
+ }
+
+ own_addr_type = parse_arg_kv_dflt("own_addr_type", cmd_own_addr_types,
+ BLE_OWN_ADDR_PUBLIC, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'own_addr_type' parameter\n");
+ return rc;
+ }
+
+ duration_ms = parse_arg_long_bounds_dflt("duration", 1, INT32_MAX, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'duration' parameter\n");
+ return rc;
+ }
+
+ phy_1M_params.scan_itvl = parse_arg_time_dflt("scan_interval", 625, 0x0010, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'scan_interval' parameter\n");
+ return rc;
+ }
+
+ phy_1M_params.scan_window = parse_arg_time_dflt("scan_window", 625, 0x0010, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'scan_window' parameter\n");
+ return rc;
+ }
+
+ phy_1M_params.itvl_min = parse_arg_time_dflt("interval_min", 1250,
+ BLE_GAP_INITIAL_CONN_ITVL_MIN,
+ &rc);
+ if (rc != 0) {
+ console_printf("invalid 'interval_min' parameter\n");
+ return rc;
+ }
+
+ phy_1M_params.itvl_max = parse_arg_time_dflt("interval_max", 1250,
+ BLE_GAP_INITIAL_CONN_ITVL_MAX,
+ &rc);
+ if (rc != 0) {
+ console_printf("invalid 'interval_max' parameter\n");
+ return rc;
+ }
+
+ phy_1M_params.latency = parse_arg_uint16_dflt("latency", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'latency' parameter\n");
+ return rc;
+ }
+
+ phy_1M_params.supervision_timeout = parse_arg_time_dflt("timeout", 10000,
+ 0x0100, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'timeout' parameter\n");
+ return rc;
+ }
+
+ phy_1M_params.min_ce_len = parse_arg_time_dflt("min_conn_event_len", 625,
+ 0x0010, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'min_conn_event_len' parameter\n");
+ return rc;
+ }
+
+ phy_1M_params.max_ce_len = parse_arg_time_dflt("max_conn_event_len", 625,
+ 0x0300, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'max_conn_event_len' parameter\n");
+ return rc;
+ }
+
+ if (ext == 0x00) {
+ rc = btshell_conn_initiate(own_addr_type, peer_addr_param, duration_ms,
+ &phy_1M_params);
+ if (rc) {
+ console_printf("error connecting; rc=%d\n", rc);
+ }
+ return rc;
+ }
+
+ if (ext == 0x01) {
+ rc = btshell_ext_conn_initiate(own_addr_type, peer_addr_param,
+ duration_ms, &phy_1M_params,
+ NULL, NULL);
+ if (rc) {
+ console_printf("error connecting; rc=%d\n", rc);
+ }
+ return rc;
+ }
+
+ /* Get coded params */
+ phy_coded_params.scan_itvl = parse_arg_time_dflt("coded_scan_interval",
+ 625, 0x0010, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'coded_scan_interval' parameter\n");
+ return rc;
+ }
+
+ phy_coded_params.scan_window = parse_arg_time_dflt("coded_scan_window",
+ 625, 0x0010, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'coded_scan_window' parameter\n");
+ return rc;
+ }
+
+ phy_coded_params.itvl_min = parse_arg_time_dflt("coded_interval_min", 1250,
+ BLE_GAP_INITIAL_CONN_ITVL_MIN,
+ &rc);
+ if (rc != 0) {
+ console_printf("invalid 'coded_interval_min' parameter\n");
+ return rc;
+ }
+
+ phy_coded_params.itvl_max = parse_arg_time_dflt("coded_interval_max", 1250,
+ BLE_GAP_INITIAL_CONN_ITVL_MAX,
+ &rc);
+ if (rc != 0) {
+ console_printf("invalid 'coded_interval_max' parameter\n");
+ return rc;
+ }
+
+ phy_coded_params.latency =
+ parse_arg_uint16_dflt("coded_latency", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'coded_latency' parameter\n");
+ return rc;
+ }
+
+ phy_coded_params.supervision_timeout =
+ parse_arg_time_dflt("coded_timeout", 10000, 0x0100, &rc);
+
+ if (rc != 0) {
+ console_printf("invalid 'coded_timeout' parameter\n");
+ return rc;
+ }
+
+ phy_coded_params.min_ce_len =
+ parse_arg_time_dflt("coded_min_conn_event", 625, 0x0010, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'coded_min_conn_event' parameter\n");
+ return rc;
+ }
+
+ phy_coded_params.max_ce_len = parse_arg_time_dflt("coded_max_conn_event",
+ 625, 0x0300, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'coded_max_conn_event' parameter\n");
+ return rc;
+ }
+
+ /* Get 2M params */
+ phy_2M_params.itvl_min = parse_arg_time_dflt("2M_interval_min", 1250,
+ BLE_GAP_INITIAL_CONN_ITVL_MIN,
+ &rc);
+ if (rc != 0) {
+ console_printf("invalid '2M_interval_min' parameter\n");
+ return rc;
+ }
+
+ phy_2M_params.itvl_max = parse_arg_time_dflt("2M_interval_max", 1250,
+ BLE_GAP_INITIAL_CONN_ITVL_MAX, &rc);
+ if (rc != 0) {
+ console_printf("invalid '2M_interval_max' parameter\n");
+ return rc;
+ }
+
+ phy_2M_params.latency =
+ parse_arg_uint16_dflt("2M_latency", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid '2M_latency' parameter\n");
+ return rc;
+ }
+
+ phy_2M_params.supervision_timeout = parse_arg_time_dflt("2M_timeout", 10000,
+ 0x0100, &rc);
+
+ if (rc != 0) {
+ console_printf("invalid '2M_timeout' parameter\n");
+ return rc;
+ }
+
+ phy_2M_params.min_ce_len = parse_arg_time_dflt("2M_min_conn_event", 625,
+ 0x0010, &rc);
+ if (rc != 0) {
+ console_printf("invalid '2M_min_conn_event' parameter\n");
+ return rc;
+ }
+
+ phy_2M_params.max_ce_len = parse_arg_time_dflt("2M_max_conn_event", 625,
+ 0x0300, &rc);
+ if (rc != 0) {
+ console_printf("invalid '2M_max_conn_event' parameter\n");
+ return rc;
+ }
+
+ if (ext == 0x02) {
+ rc = btshell_ext_conn_initiate(own_addr_type, peer_addr_param,
+ duration_ms, NULL, NULL, &phy_coded_params);
+ return rc;
+ }
+
+ if (ext == 0x03) {
+ rc = btshell_ext_conn_initiate(own_addr_type, peer_addr_param,
+ duration_ms, &phy_1M_params, NULL,
+ &phy_coded_params);
+ return rc;
+ }
+
+ rc = btshell_ext_conn_initiate(own_addr_type, peer_addr_param,
+ duration_ms, &phy_1M_params,
+ &phy_2M_params,
+ &phy_coded_params);
+ return rc;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param connect_params[] = {
+ {"cancel", "cancel connection procedure"},
+ {"extended", "usage: =[none|1M|coded|both|all], default: none"},
+ {"peer_addr_type", "usage: =[public|random|public_id|random_id], default: public"},
+ {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"},
+ {"own_addr_type", "usage: =[public|random|rpa_pub|rpa_rnd], default: public"},
+ {"duration", "usage: =[1-INT32_MAX], default: 0"},
+ {"scan_interval", "usage: =[0-UINT16_MAX], default: 0x0010"},
+ {"scan_window", "usage: =[0-UINT16_MAX], default: 0x0010"},
+ {"interval_min", "usage: =[0-UINT16_MAX], default: 30"},
+ {"interval_max", "usage: =[0-UINT16_MAX], default: 50"},
+ {"latency", "usage: =[UINT16], default: 0"},
+ {"timeout", "usage: =[UINT16], default: 0x0100"},
+ {"min_conn_event_len", "usage: =[UINT16], default: 0x0010"},
+ {"max_conn_event_len", "usage: =[UINT16], default: 0x0300"},
+ {"coded_scan_interval", "usage: =[0-UINT16_MAX], default: 0x0010"},
+ {"coded_scan_window", "usage: =[0-UINT16_MAX], default: 0x0010"},
+ {"coded_interval_min", "usage: =[0-UINT16_MAX], default: 30"},
+ {"coded_interval_max", "usage: =[0-UINT16_MAX], default: 50"},
+ {"coded_latency", "usage: =[UINT16], default: 0"},
+ {"coded_timeout", "usage: =[UINT16], default: 0x0100"},
+ {"coded_min_conn_event_len", "usage: =[UINT16], default: 0x0010"},
+ {"coded_max_conn_event_len", "usage: =[UINT16], default: 0x0300"},
+ {"2M_interval_min", "usage: =[0-UINT16_MAX], default: 30"},
+ {"2M_interval_max", "usage: =[0-UINT16_MAX], default: 50"},
+ {"2M_latency", "usage: =[UINT16], default: 0"},
+ {"2M_timeout", "usage: =[UINT16], default: 0x0100"},
+ {"2M_min_conn_event_len", "usage: =[UINT16], default: 0x0010"},
+ {"2M_max_conn_event_len", "usage: =[UINT16], default: 0x0300"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help connect_help = {
+ .summary = "start/stop connection procedure with specific parameters",
+ .usage = NULL,
+ .params = connect_params,
+};
+#endif
+
+/*****************************************************************************
+ * $disconnect *
+ *****************************************************************************/
+
+static int
+cmd_disconnect(int argc, char **argv)
+{
+ uint16_t conn_handle;
+ uint8_t reason;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ reason = parse_arg_uint8_dflt("reason", BLE_ERR_REM_USER_CONN_TERM, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'reason' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_term_conn(conn_handle, reason);
+ if (rc != 0) {
+ console_printf("error terminating connection; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+cmd_show_conn(int argc, char **argv)
+{
+ struct ble_gap_conn_desc conn_desc;
+ struct btshell_conn *conn;
+ int rc;
+ int i;
+
+ for (i = 0; i < btshell_num_conns; i++) {
+ conn = btshell_conns + i;
+
+ rc = ble_gap_conn_find(conn->handle, &conn_desc);
+ if (rc == 0) {
+ print_conn_desc(&conn_desc);
+ }
+ }
+
+ return 0;
+}
+
+static int
+cmd_show_addr(int argc, char **argv)
+{
+ uint8_t id_addr[6];
+ int rc;
+
+ console_printf("public_id_addr=");
+ rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, id_addr, NULL);
+ if (rc == 0) {
+ print_addr(id_addr);
+ } else {
+ console_printf("none");
+ }
+
+ console_printf(" random_id_addr=");
+ rc = ble_hs_id_copy_addr(BLE_ADDR_RANDOM, id_addr, NULL);
+ if (rc == 0) {
+ print_addr(id_addr);
+ } else {
+ console_printf("none");
+ }
+ console_printf("\n");
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param disconnect_params[] = {
+ {"conn", "connection handle parameter, usage: =<UINT16>"},
+ {"reason", "disconnection reason, usage: =[UINT8], default: 19 (remote user terminated connection)"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help disconnect_help = {
+ .summary = "disconnect command",
+ .usage = NULL,
+ .params = disconnect_params,
+};
+#endif
+
+/*****************************************************************************
+ * $set-scan-opts *
+ *****************************************************************************/
+
+static struct btshell_scan_opts g_scan_opts = {
+ .limit = UINT16_MAX,
+ .ignore_legacy = 0,
+};
+
+static int
+cmd_set_scan_opts(int argc, char **argv)
+{
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ g_scan_opts.limit = parse_arg_uint16_dflt("decode_limit", UINT16_MAX, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'decode_limit' parameter\n");
+ return rc;
+ }
+
+ g_scan_opts.ignore_legacy = parse_arg_bool_dflt("ignore_legacy", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'ignore_legacy' parameter\n");
+ return rc;
+ }
+
+ g_scan_opts.periodic_only = parse_arg_bool_dflt("periodic_only", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'periodic_only' parameter\n");
+ return rc;
+ }
+
+ return rc;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param set_scan_opts_params[] = {
+ {"decode_limit", "usage: =[0-UINT16_MAX], default: UINT16_MAX"},
+ {"ignore_legacy", "usage: =[0-1], default: 0"},
+ {"periodic_only", "usage: =[0-1], default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help set_scan_opts_help = {
+ .summary = "set scan options",
+ .usage = NULL,
+ .params = set_scan_opts_params,
+};
+#endif
+
+/*****************************************************************************
+ * $scan *
+ *****************************************************************************/
+
+static const struct kv_pair cmd_scan_filt_policies[] = {
+ { "no_wl", BLE_HCI_SCAN_FILT_NO_WL },
+ { "use_wl", BLE_HCI_SCAN_FILT_USE_WL },
+ { "no_wl_inita", BLE_HCI_SCAN_FILT_NO_WL_INITA },
+ { "use_wl_inita", BLE_HCI_SCAN_FILT_USE_WL_INITA },
+ { NULL }
+};
+
+static struct kv_pair cmd_scan_ext_types[] = {
+ { "none", 0x00 },
+ { "1M", 0x01 },
+ { "coded", 0x02 },
+ { "both", 0x03 },
+ { NULL }
+};
+
+static struct btshell_scan_opts g_scan_opts;
+
+static int
+cmd_scan(int argc, char **argv)
+{
+ struct ble_gap_disc_params params = {0};
+ struct ble_gap_ext_disc_params uncoded = {0};
+ struct ble_gap_ext_disc_params coded = {0};
+ uint8_t extended;
+ int32_t duration_ms;
+ uint8_t own_addr_type;
+ uint16_t duration;
+ uint16_t period;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (argc > 1 && strcmp(argv[1], "cancel") == 0) {
+ rc = btshell_scan_cancel();
+ if (rc != 0) {
+ console_printf("scan cancel fail: %d\n", rc);
+ return rc;
+ }
+ return 0;
+ }
+
+ extended = parse_arg_kv_dflt("extended", cmd_scan_ext_types, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'extended' parameter\n");
+ return rc;
+ }
+
+ duration_ms = parse_arg_time_dflt("duration", 10000, BLE_HS_FOREVER, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'duration' parameter\n");
+ return rc;
+ }
+
+ params.limited = parse_arg_bool_dflt("limited", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'limited' parameter\n");
+ return rc;
+ }
+
+ params.passive = parse_arg_bool_dflt("passive", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'passive' parameter\n");
+ return rc;
+ }
+
+ params.itvl = parse_arg_time_dflt("interval", 625, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'interval' parameter\n");
+ return rc;
+ }
+
+ params.window = parse_arg_time_dflt("window", 625, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'window' parameter\n");
+ return rc;
+ }
+
+ params.filter_policy = parse_arg_kv_dflt("filter", cmd_scan_filt_policies,
+ BLE_HCI_SCAN_FILT_NO_WL, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'filter' parameter\n");
+ return rc;
+ }
+
+ params.filter_duplicates = parse_arg_bool_dflt("nodups", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'nodups' parameter\n");
+ return rc;
+ }
+
+ own_addr_type = parse_arg_kv_dflt("own_addr_type", cmd_own_addr_types,
+ BLE_OWN_ADDR_PUBLIC, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'own_addr_type' parameter\n");
+ return rc;
+ }
+
+ if (extended == 0) {
+ rc = btshell_scan(own_addr_type, duration_ms, &params, &g_scan_opts);
+ if (rc != 0) {
+ console_printf("error scanning; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+ }
+
+ /* Copy above parameters to uncoded params */
+ uncoded.passive = params.passive;
+ uncoded.itvl = params.itvl;
+ uncoded.window = params.window;
+
+ duration = parse_arg_time_dflt("extended_duration", 10000, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'extended_duration' parameter\n");
+ return rc;
+ }
+
+ period = parse_arg_time_dflt("extended_period", 1280000, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'extended_period' parameter\n");
+ return rc;
+ }
+
+ coded.itvl = parse_arg_time_dflt("longrange_interval", 625, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'longrange_interval' parameter\n");
+ return rc;
+ }
+
+ coded.window = parse_arg_time_dflt("longrange_window", 625, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'longrange_window' parameter\n");
+ return rc;
+ }
+
+ coded.passive = parse_arg_uint16_dflt("longrange_passive", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'longrange_passive' parameter\n");
+ return rc;
+ }
+
+ switch (extended) {
+ case 0x01:
+ rc = btshell_ext_scan(own_addr_type, duration, period,
+ params.filter_duplicates, params.filter_policy,
+ params.limited, &uncoded, NULL,
+ &g_scan_opts);
+ break;
+ case 0x02:
+ rc = btshell_ext_scan(own_addr_type, duration, period,
+ params.filter_duplicates, params.filter_policy,
+ params.limited, NULL, &coded,
+ &g_scan_opts);
+ break;
+ case 0x03:
+ rc = btshell_ext_scan(own_addr_type, duration, period,
+ params.filter_duplicates, params.filter_policy,
+ params.limited, &uncoded, &coded,
+ &g_scan_opts);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ if (rc != 0) {
+ console_printf("error scanning; rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param scan_params[] = {
+ {"cancel", "cancel scan procedure"},
+ {"extended", "usage: =[none|1M|coded|both], default: none"},
+ {"duration", "usage: =[1-INT32_MAX], default: INT32_MAX"},
+ {"limited", "usage: =[0-1], default: 0"},
+ {"passive", "usage: =[0-1], default: 0"},
+ {"interval", "usage: =[0-UINT16_MAX], default: 0"},
+ {"window", "usage: =[0-UINT16_MAX], default: 0"},
+ {"filter", "usage: =[no_wl|use_wl|no_wl_inita|use_wl_inita], default: no_wl"},
+ {"nodups", "usage: =[0-1], default: 0"},
+ {"own_addr_type", "usage: =[public|random|rpa_pub|rpa_rnd], default: public"},
+ {"extended_duration", "usage: =[0-UINT16_MAX], default: 0"},
+ {"extended_period", "usage: =[0-UINT16_MAX], default: 0"},
+ {"longrange_interval", "usage: =[0-UINT16_MAX], default: 0"},
+ {"longrange_window", "usage: =[0-UINT16_MAX], default: 0"},
+ {"longrange_passive", "usage: =[0-1], default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help scan_help = {
+ .summary = "start/stop scan procedure with specific parameters",
+ .usage = NULL,
+ .params = scan_params,
+};
+#endif
+
+/*****************************************************************************
+ * $set *
+ *****************************************************************************/
+
+static int
+cmd_set_addr(void)
+{
+ ble_addr_t addr;
+ int rc;
+
+ rc = parse_dev_addr(NULL, cmd_addr_type, &addr);
+ if (rc != 0) {
+ console_printf("invalid 'addr' parameter\n");
+ return rc;
+ }
+
+ switch (addr.type) {
+#if MYNEWT_VAL(BLE_CONTROLLER)
+ case BLE_ADDR_PUBLIC:
+ /* We shouldn't be writing to the controller's address (g_dev_addr).
+ * There is no standard way to set the local public address, so this is
+ * our only option at the moment.
+ */
+ memcpy(g_dev_addr, addr.val, 6);
+ ble_hs_id_set_pub(g_dev_addr);
+ break;
+#endif
+
+ case BLE_ADDR_RANDOM:
+ rc = ble_hs_id_set_rnd(addr.val);
+ if (rc != 0) {
+ return rc;
+ }
+ break;
+
+ default:
+ return BLE_HS_EUNKNOWN;
+ }
+
+ return 0;
+}
+
+static int
+cmd_set(int argc, char **argv)
+{
+ uint16_t mtu;
+ uint8_t irk[16];
+ int good = 0;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = parse_arg_find_idx("addr");
+ if (rc != -1) {
+ rc = cmd_set_addr();
+ if (rc != 0) {
+ return rc;
+ }
+ good = 1;
+ }
+
+ mtu = parse_arg_uint16("mtu", &rc);
+ if (rc == 0) {
+ rc = ble_att_set_preferred_mtu(mtu);
+ if (rc == 0) {
+ good = 1;
+ }
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'mtu' parameter\n");
+ return rc;
+ }
+
+ rc = parse_arg_byte_stream_exact_length("irk", irk, 16);
+ if (rc == 0) {
+ good = 1;
+ ble_hs_pvcy_set_our_irk(irk);
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'irk' parameter\n");
+ return rc;
+ }
+
+ if (!good) {
+ console_printf("Error: no valid settings specified\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param set_params[] = {
+ {"addr", "set device address, usage: =[XX:XX:XX:XX:XX:XX]"},
+ {"addr_type", "set device address type, usage: =[public|random], default: public"},
+ {"mtu", "Maximum Transimssion Unit, usage: =[0-UINT16_MAX]"},
+ {"irk", "Identity Resolving Key, usage: =[XX:XX...], len=16 octets"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help set_help = {
+ .summary = "set device parameters",
+ .usage = NULL,
+ .params = set_params,
+};
+#endif
+
+/*****************************************************************************
+ * $set-adv-data *
+ *****************************************************************************/
+
+#define CMD_ADV_DATA_MAX_UUIDS16 8
+#define CMD_ADV_DATA_MAX_UUIDS32 8
+#define CMD_ADV_DATA_MAX_UUIDS128 2
+#define CMD_ADV_DATA_MAX_PUBLIC_TGT_ADDRS 8
+#define CMD_ADV_DATA_SVC_DATA_UUID16_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ
+#define CMD_ADV_DATA_SVC_DATA_UUID32_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ
+#define CMD_ADV_DATA_SVC_DATA_UUID128_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ
+#define CMD_ADV_DATA_URI_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ
+#define CMD_ADV_DATA_MFG_DATA_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ
+
+#if MYNEWT_VAL(BLE_EXT_ADV)
+static void
+update_pattern(uint8_t *buf, int counter)
+{
+ int i;
+
+ for (i = 0; i < 10; i += 2) {
+ counter += 2;
+ buf[i] = (counter / 1000) << 4 | (counter / 100 % 10);
+ buf[i + 1] = (counter / 10 % 10) << 4 | (counter % 10);
+ }
+}
+#endif
+
+static int
+cmd_set_adv_data_or_scan_rsp(int argc, char **argv, bool scan_rsp,
+ bool periodic)
+{
+ static bssnz_t ble_uuid16_t uuids16[CMD_ADV_DATA_MAX_UUIDS16];
+ static bssnz_t ble_uuid32_t uuids32[CMD_ADV_DATA_MAX_UUIDS32];
+ static bssnz_t ble_uuid128_t uuids128[CMD_ADV_DATA_MAX_UUIDS128];
+ static bssnz_t uint8_t
+ public_tgt_addrs[CMD_ADV_DATA_MAX_PUBLIC_TGT_ADDRS]
+ [BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN];
+ static bssnz_t uint8_t slave_itvl_range[BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN];
+ static bssnz_t uint8_t
+ svc_data_uuid16[CMD_ADV_DATA_SVC_DATA_UUID16_MAX_LEN];
+ static bssnz_t uint8_t
+ svc_data_uuid32[CMD_ADV_DATA_SVC_DATA_UUID32_MAX_LEN];
+ static bssnz_t uint8_t
+ svc_data_uuid128[CMD_ADV_DATA_SVC_DATA_UUID128_MAX_LEN];
+ static bssnz_t uint8_t uri[CMD_ADV_DATA_URI_MAX_LEN];
+ static bssnz_t uint8_t mfg_data[CMD_ADV_DATA_MFG_DATA_MAX_LEN];
+ struct ble_hs_adv_fields adv_fields;
+ uint32_t uuid32;
+ uint16_t uuid16;
+ uint8_t uuid128[16];
+ uint8_t public_tgt_addr[BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN];
+ uint8_t eddystone_url_body_len;
+ uint8_t eddystone_url_suffix;
+ uint8_t eddystone_url_scheme;
+ int8_t eddystone_measured_power = 0;
+ char eddystone_url_body[BLE_EDDYSTONE_URL_MAX_LEN];
+ char *eddystone_url_full;
+ int svc_data_uuid16_len;
+ int svc_data_uuid32_len;
+ int svc_data_uuid128_len;
+ int uri_len;
+ int mfg_data_len;
+ int tmp;
+ int rc;
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ uint8_t instance;
+ uint8_t extra_data[10];
+ uint16_t counter;
+ uint16_t extra_data_len;
+ struct os_mbuf *adv_data;
+#endif
+
+ /* cannot set scan rsp for periodic */
+ if (scan_rsp && periodic) {
+ return -1;
+ }
+
+ memset(&adv_fields, 0, sizeof adv_fields);
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ instance = parse_arg_uint8_dflt("instance", 0, &rc);
+ if (rc != 0 || instance >= BLE_ADV_INSTANCES) {
+ console_printf("invalid instance\n");
+ return rc;
+ }
+#endif
+
+ tmp = parse_arg_uint8("flags", &rc);
+ if (rc == 0) {
+ adv_fields.flags = tmp;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'flags' parameter\n");
+ return rc;
+ }
+
+ while (1) {
+ uuid16 = parse_arg_uint16("uuid16", &rc);
+ if (rc == 0) {
+ if (adv_fields.num_uuids16 >= CMD_ADV_DATA_MAX_UUIDS16) {
+ console_printf("invalid 'uuid16' parameter\n");
+ return EINVAL;
+ }
+ uuids16[adv_fields.num_uuids16] = (ble_uuid16_t) BLE_UUID16_INIT(uuid16);
+ adv_fields.num_uuids16++;
+ } else if (rc == ENOENT) {
+ break;
+ } else {
+ console_printf("invalid 'uuid16' parameter\n");
+ return rc;
+ }
+ }
+ if (adv_fields.num_uuids16 > 0) {
+ adv_fields.uuids16 = uuids16;
+ }
+
+ tmp = parse_arg_bool_dflt("uuids16_is_complete", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'uuids16_is_complete' parameter\n");
+ return rc;
+ }
+
+ while (1) {
+ uuid32 = parse_arg_uint32("uuid32", &rc);
+ if (rc == 0) {
+ if (adv_fields.num_uuids32 >= CMD_ADV_DATA_MAX_UUIDS32) {
+ console_printf("invalid 'uuid32' parameter\n");
+ return EINVAL;
+ }
+ uuids32[adv_fields.num_uuids32] = (ble_uuid32_t) BLE_UUID32_INIT(uuid32);
+ adv_fields.num_uuids32++;
+ } else if (rc == ENOENT) {
+ break;
+ } else {
+ console_printf("invalid 'uuid32' parameter\n");
+ return rc;
+ }
+ }
+ if (adv_fields.num_uuids32 > 0) {
+ adv_fields.uuids32 = uuids32;
+ }
+
+ tmp = parse_arg_bool_dflt("uuids32_is_complete", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'uuids32_is_complete' parameter\n");
+ return rc;
+ }
+
+ while (1) {
+ rc = parse_arg_byte_stream_exact_length("uuid128", uuid128, 16);
+ if (rc == 0) {
+ if (adv_fields.num_uuids128 >= CMD_ADV_DATA_MAX_UUIDS128) {
+ console_printf("invalid 'uuid128' parameter\n");
+ return EINVAL;
+ }
+ ble_uuid_init_from_buf((ble_uuid_any_t *) &uuids128[adv_fields.num_uuids128],
+ uuid128, 16);
+ adv_fields.num_uuids128++;
+ } else if (rc == ENOENT) {
+ break;
+ } else {
+ console_printf("invalid 'uuid128' parameter\n");
+ return rc;
+ }
+ }
+ if (adv_fields.num_uuids128 > 0) {
+ adv_fields.uuids128 = uuids128;
+ }
+
+ tmp = parse_arg_bool_dflt("uuids128_is_complete", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'uuids128_is_complete' parameter\n");
+ return rc;
+ }
+
+ adv_fields.name = (uint8_t *)parse_arg_extract("name");
+ if (adv_fields.name != NULL) {
+ adv_fields.name_len = strlen((char *)adv_fields.name);
+ }
+
+ tmp = parse_arg_long_bounds("tx_power_level", INT8_MIN, INT8_MAX, &rc);
+ if (rc == 0) {
+ adv_fields.tx_pwr_lvl = tmp;
+ adv_fields.tx_pwr_lvl_is_present = 1;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'tx_power_level' parameter\n");
+ return rc;
+ }
+
+ rc = parse_arg_byte_stream_exact_length("slave_interval_range",
+ slave_itvl_range,
+ BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
+ if (rc == 0) {
+ adv_fields.slave_itvl_range = slave_itvl_range;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'slave_interval_range' parameter\n");
+ return rc;
+ }
+
+ rc = parse_arg_byte_stream("service_data_uuid16",
+ CMD_ADV_DATA_SVC_DATA_UUID16_MAX_LEN,
+ svc_data_uuid16, &svc_data_uuid16_len);
+ if (rc == 0) {
+ adv_fields.svc_data_uuid16 = svc_data_uuid16;
+ adv_fields.svc_data_uuid16_len = svc_data_uuid16_len;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'service_data_uuid16' parameter\n");
+ return rc;
+ }
+
+ while (1) {
+ rc = parse_arg_byte_stream_exact_length(
+ "public_target_address", public_tgt_addr,
+ BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN);
+ if (rc == 0) {
+ if (adv_fields.num_public_tgt_addrs >=
+ CMD_ADV_DATA_MAX_PUBLIC_TGT_ADDRS) {
+
+ console_printf("invalid 'public_target_address' parameter\n");
+ return EINVAL;
+ }
+ memcpy(public_tgt_addrs[adv_fields.num_public_tgt_addrs],
+ public_tgt_addr, BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN);
+ adv_fields.num_public_tgt_addrs++;
+ } else if (rc == ENOENT) {
+ break;
+ } else {
+ console_printf("invalid 'public_target_address' parameter\n");
+ return rc;
+ }
+ }
+ if (adv_fields.num_public_tgt_addrs > 0) {
+ adv_fields.public_tgt_addr = (void *)public_tgt_addrs;
+ }
+
+ adv_fields.appearance = parse_arg_uint16("appearance", &rc);
+ if (rc == 0) {
+ adv_fields.appearance_is_present = 1;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'appearance' parameter\n");
+ return rc;
+ }
+
+ adv_fields.adv_itvl = parse_arg_uint16("advertising_interval", &rc);
+ if (rc == 0) {
+ adv_fields.adv_itvl_is_present = 1;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'advertising_interval' parameter\n");
+ return rc;
+ }
+
+ rc = parse_arg_byte_stream("service_data_uuid32",
+ CMD_ADV_DATA_SVC_DATA_UUID32_MAX_LEN,
+ svc_data_uuid32, &svc_data_uuid32_len);
+ if (rc == 0) {
+ adv_fields.svc_data_uuid32 = svc_data_uuid32;
+ adv_fields.svc_data_uuid32_len = svc_data_uuid32_len;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'service_data_uuid32' parameter\n");
+ return rc;
+ }
+
+ rc = parse_arg_byte_stream("service_data_uuid128",
+ CMD_ADV_DATA_SVC_DATA_UUID128_MAX_LEN,
+ svc_data_uuid128, &svc_data_uuid128_len);
+ if (rc == 0) {
+ adv_fields.svc_data_uuid128 = svc_data_uuid128;
+ adv_fields.svc_data_uuid128_len = svc_data_uuid128_len;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'service_data_uuid128' parameter\n");
+ return rc;
+ }
+
+ rc = parse_arg_byte_stream("uri", CMD_ADV_DATA_URI_MAX_LEN, uri, &uri_len);
+ if (rc == 0) {
+ adv_fields.uri = uri;
+ adv_fields.uri_len = uri_len;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'uri' parameter\n");
+ return rc;
+ }
+
+ rc = parse_arg_byte_stream("mfg_data", CMD_ADV_DATA_MFG_DATA_MAX_LEN,
+ mfg_data, &mfg_data_len);
+ if (rc == 0) {
+ adv_fields.mfg_data = mfg_data;
+ adv_fields.mfg_data_len = mfg_data_len;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'mfg_data' parameter\n");
+ return rc;
+ }
+
+ tmp = parse_arg_long_bounds("eddystone_measured_power", -100, 20, &rc);
+ if (rc == 0) {
+ eddystone_measured_power = tmp;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'eddystone_measured_power' parameter\n");
+ return rc;
+ }
+
+ eddystone_url_full = parse_arg_extract("eddystone_url");
+ if (eddystone_url_full != NULL) {
+ rc = parse_eddystone_url(eddystone_url_full, &eddystone_url_scheme,
+ eddystone_url_body,
+ &eddystone_url_body_len,
+ &eddystone_url_suffix);
+ if (rc != 0) {
+ goto done;
+ }
+
+ rc = ble_eddystone_set_adv_data_url(&adv_fields, eddystone_url_scheme,
+ eddystone_url_body,
+ eddystone_url_body_len,
+ eddystone_url_suffix,
+ eddystone_measured_power);
+ } else {
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ /* Default to legacy PDUs size, mbuf chain will be increased if needed
+ */
+ adv_data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0);
+ if (!adv_data) {
+ rc = ENOMEM;
+ goto done;
+ }
+
+ rc = ble_hs_adv_set_fields_mbuf(&adv_fields, adv_data);
+ if (rc) {
+ os_mbuf_free_chain(adv_data);
+ goto done;
+ }
+
+ /* Append some extra data, if requested */
+ extra_data_len = parse_arg_uint16("extra_data_len", &rc);
+ if (rc == 0) {
+ counter = 0;
+ extra_data_len = min(extra_data_len, 1650);
+ while (counter < extra_data_len) {
+ update_pattern(extra_data, counter);
+
+ rc = os_mbuf_append(adv_data, extra_data,
+ min(extra_data_len - counter, 10));
+ if (rc) {
+ os_mbuf_free_chain(adv_data);
+ goto done;
+ }
+
+ counter += 10;
+ }
+ }
+
+ if (scan_rsp) {
+ rc = ble_gap_ext_adv_rsp_set_data(instance, adv_data);
+#if MYNEWT_VAL(BLE_PERIODIC_ADV)
+ } else if (periodic) {
+ rc = ble_gap_periodic_adv_set_data(instance, adv_data);
+#endif
+ } else {
+ rc = ble_gap_ext_adv_set_data(instance, adv_data);
+ }
+#else
+ if (scan_rsp) {
+ rc = ble_gap_adv_rsp_set_fields(&adv_fields);
+ } else {
+ rc = ble_gap_adv_set_fields(&adv_fields);
+ }
+#endif
+ }
+done:
+ if (rc != 0) {
+ console_printf("error setting advertisement data; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+cmd_set_adv_data(int argc, char **argv)
+{
+ return cmd_set_adv_data_or_scan_rsp(argc, argv, false, false);
+}
+
+static int
+cmd_set_scan_rsp(int argc, char **argv)
+{
+ return cmd_set_adv_data_or_scan_rsp(argc, argv, true, false);
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param set_adv_data_params[] = {
+ {"instance", "default: 0"},
+ {"flags", "usage: =[0-UINT8_MAX]"},
+ {"uuid16", "usage: =[UINT16]"},
+ {"uuid16_is_complete", "usage: =[0-1], default=0"},
+ {"uuid32", "usage: =[UINT32]"},
+ {"uuid32_is_complete", "usage: =[0-1], default=0"},
+ {"uuid128", "usage: =[XX:XX...], len=16 octets"},
+ {"uuid128_is_complete", "usage: =[0-1], default=0"},
+ {"tx_power_level", "usage: =[INT8_MIN-INT8_MAX]"},
+ {"slave_interval_range", "usage: =[XX:XX:XX:XX]"},
+ {"public_target_address", "usage: =[XX:XX:XX:XX:XX:XX]"},
+ {"appearance", "usage: =[UINT16]"},
+ {"name", "usage: =[string]"},
+ {"advertising_interval", "usage: =[UINT16]"},
+ {"service_data_uuid16", "usage: =[XX:XX...]"},
+ {"service_data_uuid32", "usage: =[XX:XX...]"},
+ {"service_data_uuid128", "usage: =[XX:XX...]"},
+ {"uri", "usage: =[XX:XX...]"},
+ {"mfg_data", "usage: =[XX:XX...]"},
+ {"measured_power", "usage: =[-100-20]"},
+ {"eddystone_url", "usage: =[string]"},
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ {"extra_data_len", "usage: =[UINT16]"},
+#endif
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help set_adv_data_help = {
+ .summary = "set advertising data",
+ .usage = NULL,
+ .params = set_adv_data_params,
+};
+
+static const struct shell_cmd_help set_scan_rsp_help = {
+ .summary = "set scan response",
+ .usage = NULL,
+ .params = set_adv_data_params,
+};
+#endif
+
+/*****************************************************************************
+ * $set-priv-mode *
+ *****************************************************************************/
+
+static int
+cmd_set_priv_mode(int argc, char **argv)
+{
+ ble_addr_t addr;
+ uint8_t priv_mode;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = parse_dev_addr(NULL, cmd_addr_type, &addr);
+ if (rc != 0) {
+ console_printf("invalid 'addr' parameter\n");
+ return rc;
+ }
+
+ priv_mode = parse_arg_uint8("mode", &rc);
+ if (rc != 0) {
+ console_printf("missing mode\n");
+ return rc;
+ }
+
+ return ble_gap_set_priv_mode(&addr, priv_mode);
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param set_priv_mode_params[] = {
+ {"addr", "set priv mode for device address, usage: =[XX:XX:XX:XX:XX:XX]"},
+ {"addr_type", "set priv mode for device address type, usage: =[public|random], default: public"},
+ {"mode", "set priv mode, usage: =[0-UINT8_MAX]"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help set_priv_mode_help = {
+ .summary = "set priv mode",
+ .usage = NULL,
+ .params = set_priv_mode_params,
+};
+#endif
+
+/*****************************************************************************
+ * $white-list *
+ *****************************************************************************/
+
+#define CMD_WL_MAX_SZ 8
+
+static int
+cmd_white_list(int argc, char **argv)
+{
+ static ble_addr_t addrs[CMD_WL_MAX_SZ];
+ int addrs_cnt;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ addrs_cnt = 0;
+ while (1) {
+ if (addrs_cnt >= CMD_WL_MAX_SZ) {
+ return EINVAL;
+ }
+
+ rc = parse_dev_addr(NULL, cmd_addr_type, &addrs[addrs_cnt]);
+ if (rc == ENOENT) {
+ break;
+ } else if (rc != 0) {
+ console_printf("invalid 'addr' parameter #%d\n", addrs_cnt + 1);
+ return rc;
+ }
+
+ addrs_cnt++;
+ }
+
+ if (addrs_cnt == 0) {
+ return EINVAL;
+ }
+
+ btshell_wl_set(addrs, addrs_cnt);
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param white_list_params[] = {
+ {"addr", "white-list device addresses, usage: =[XX:XX:XX:XX:XX:XX]"},
+ {"addr_type", "white-list address types, usage: =[public|random]"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help white_list_help = {
+ .summary = "set white-list addresses",
+ .usage = NULL,
+ .params = white_list_params,
+};
+#endif
+
+/*****************************************************************************
+ * $conn-rssi *
+ *****************************************************************************/
+
+static int
+cmd_conn_rssi(int argc, char **argv)
+{
+ uint16_t conn_handle;
+ int8_t rssi;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_rssi(conn_handle, &rssi);
+ if (rc != 0) {
+ console_printf("error reading rssi; rc=%d\n", rc);
+ return rc;
+ }
+
+ console_printf("conn=%d rssi=%d\n", conn_handle, rssi);
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param conn_rssi_params[] = {
+ {"conn", "connection handle parameter, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help conn_rssi_help = {
+ .summary = "check connection rssi",
+ .usage = NULL,
+ .params = conn_rssi_params,
+};
+#endif
+
+/*****************************************************************************
+ * $conn-update-params *
+ *****************************************************************************/
+
+static int
+cmd_conn_update_params(int argc, char **argv)
+{
+ struct ble_gap_upd_params params;
+ uint16_t conn_handle;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ params.itvl_min = parse_arg_time_dflt("interval_min", 1250,
+ BLE_GAP_INITIAL_CONN_ITVL_MIN,
+ &rc);
+ if (rc != 0) {
+ console_printf("invalid 'interval_min' parameter\n");
+ return rc;
+ }
+
+ params.itvl_max = parse_arg_time_dflt("interval_max", 1250,
+ BLE_GAP_INITIAL_CONN_ITVL_MAX,
+ &rc);
+ if (rc != 0) {
+ console_printf("invalid 'interval_max' parameter\n");
+ return rc;
+ }
+
+ params.latency = parse_arg_uint16_dflt("latency", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'latency' parameter\n");
+ return rc;
+ }
+
+ params.supervision_timeout = parse_arg_time_dflt("timeout", 10000, 0x0100,
+ &rc);
+ if (rc != 0) {
+ console_printf("invalid 'timeout' parameter\n");
+ return rc;
+ }
+
+ params.min_ce_len = parse_arg_time_dflt("min_conn_event_len", 625,
+ 0x0010, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'min_conn_event_len' parameter\n");
+ return rc;
+ }
+
+ params.max_ce_len = parse_arg_time_dflt("max_conn_event_len", 625,
+ 0x0300, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'max_conn_event_len' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_update_conn(conn_handle, &params);
+ if (rc != 0) {
+ console_printf("error updating connection; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param conn_update_params_params[] = {
+ {"conn", "conn_update_paramsion handle, usage: =<UINT16>"},
+ {"interval_min", "usage: =[0-UINT16_MAX], default: 30"},
+ {"interval_max", "usage: =[0-UINT16_MAX], default: 50"},
+ {"latency", "usage: =[UINT16], default: 0"},
+ {"timeout", "usage: =[UINT16], default: 0x0100"},
+ {"min_conn_event_len", "usage: =[UINT16], default: 0x0010"},
+ {"max_conn_event_len", "usage: =[UINT16], default: 0x0300"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help conn_update_params_help = {
+ .summary = "update connection parameters",
+ .usage = "conn_update_params usage",
+ .params = conn_update_params_params,
+};
+#endif
+
+/*****************************************************************************
+ * $conn-datalen *
+ *****************************************************************************/
+
+static int
+cmd_conn_datalen(int argc, char **argv)
+{
+ uint16_t conn_handle;
+ uint16_t tx_octets;
+ uint16_t tx_time;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ tx_octets = parse_arg_uint16("octets", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'octets' parameter\n");
+ return rc;
+ }
+
+ tx_time = parse_arg_uint16("time", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'time' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_datalen(conn_handle, tx_octets, tx_time);
+ if (rc != 0) {
+ console_printf("error setting data length; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param conn_datalen_params[] = {
+ {"conn", "Connection handle, usage: =<UINT16>"},
+ {"octets", "Max payload size to include in LL Data PDU, "
+ "range=<27-251>, usage: =<UINT16>"},
+ {"time", "Max number of microseconds the controller should use to tx "
+ "single LL packet, range=<328-17040>, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help conn_datalen_help = {
+ .summary = "set data length parameters for connection",
+ .usage = NULL,
+ .params = conn_datalen_params,
+};
+#endif
+
+/*****************************************************************************
+ * keystore *
+ *****************************************************************************/
+
+static const struct kv_pair cmd_keystore_entry_type[] = {
+ { "msec", BLE_STORE_OBJ_TYPE_PEER_SEC },
+ { "ssec", BLE_STORE_OBJ_TYPE_OUR_SEC },
+ { "cccd", BLE_STORE_OBJ_TYPE_CCCD },
+ { NULL }
+};
+
+static int
+cmd_keystore_parse_keydata(int argc, char **argv, union ble_store_key *out,
+ int *obj_type)
+{
+ int rc;
+
+ memset(out, 0, sizeof(*out));
+ *obj_type = parse_arg_kv("type", cmd_keystore_entry_type, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'type' parameter\n");
+ return rc;
+ }
+
+ switch (*obj_type) {
+ case BLE_STORE_OBJ_TYPE_PEER_SEC:
+ case BLE_STORE_OBJ_TYPE_OUR_SEC:
+ rc = parse_dev_addr(NULL, cmd_addr_type, &out->sec.peer_addr);
+ if (rc != 0) {
+ console_printf("invalid 'addr' parameter\n");
+ return rc;
+ }
+
+ out->sec.ediv = parse_arg_uint16("ediv", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'ediv' parameter\n");
+ return rc;
+ }
+
+ out->sec.rand_num = parse_arg_uint64("rand", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'rand' parameter\n");
+ return rc;
+ }
+ return 0;
+
+ default:
+ return EINVAL;
+ }
+}
+
+static int
+cmd_keystore_parse_valuedata(int argc, char **argv,
+ int obj_type,
+ union ble_store_key *key,
+ union ble_store_value *out)
+{
+ int rc;
+ int valcnt = 0;
+ memset(out, 0, sizeof(*out));
+
+ switch (obj_type) {
+ case BLE_STORE_OBJ_TYPE_PEER_SEC:
+ case BLE_STORE_OBJ_TYPE_OUR_SEC:
+ rc = parse_arg_byte_stream_exact_length("ltk", out->sec.ltk, 16);
+ if (rc == 0) {
+ out->sec.ltk_present = 1;
+ swap_in_place(out->sec.ltk, 16);
+ valcnt++;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'ltk' parameter\n");
+ return rc;
+ }
+ rc = parse_arg_byte_stream_exact_length("irk", out->sec.irk, 16);
+ if (rc == 0) {
+ out->sec.irk_present = 1;
+ swap_in_place(out->sec.irk, 16);
+ valcnt++;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'irk' parameter\n");
+ return rc;
+ }
+ rc = parse_arg_byte_stream_exact_length("csrk", out->sec.csrk, 16);
+ if (rc == 0) {
+ out->sec.csrk_present = 1;
+ swap_in_place(out->sec.csrk, 16);
+ valcnt++;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'csrk' parameter\n");
+ return rc;
+ }
+ out->sec.peer_addr = key->sec.peer_addr;
+ out->sec.ediv = key->sec.ediv;
+ out->sec.rand_num = key->sec.rand_num;
+ break;
+ }
+
+ if (valcnt) {
+ return 0;
+ }
+ return -1;
+}
+
+/*****************************************************************************
+ * keystore-add *
+ *****************************************************************************/
+
+static int
+cmd_keystore_add(int argc, char **argv)
+{
+ union ble_store_key key;
+ union ble_store_value value;
+ int obj_type;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = cmd_keystore_parse_keydata(argc, argv, &key, &obj_type);
+
+ if (rc) {
+ return rc;
+ }
+
+ rc = cmd_keystore_parse_valuedata(argc, argv, obj_type, &key, &value);
+
+ if (rc) {
+ return rc;
+ }
+
+ switch(obj_type) {
+ case BLE_STORE_OBJ_TYPE_PEER_SEC:
+ rc = ble_store_write_peer_sec(&value.sec);
+ break;
+ case BLE_STORE_OBJ_TYPE_OUR_SEC:
+ rc = ble_store_write_our_sec(&value.sec);
+ break;
+ case BLE_STORE_OBJ_TYPE_CCCD:
+ rc = ble_store_write_cccd(&value.cccd);
+ break;
+ default:
+ rc = ble_store_write(obj_type, &value);
+ }
+ return rc;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param keystore_add_params[] = {
+ {"type", "entry type, usage: =<msec|ssec|cccd>"},
+ {"addr_type", "usage: =<public|random>"},
+ {"addr", "usage: =<XX:XX:XX:XX:XX:XX>"},
+ {"ediv", "usage: =<UINT16>"},
+ {"rand", "usage: =<UINT64>"},
+ {"ltk", "usage: =<XX:XX:...>, len=16 octets"},
+ {"irk", "usage: =<XX:XX:...>, len=16 octets"},
+ {"csrk", "usage: =<XX:XX:...>, len=16 octets"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help keystore_add_help = {
+ .summary = "add data to keystore",
+ .usage = NULL,
+ .params = keystore_add_params,
+};
+#endif
+
+/*****************************************************************************
+ * keystore-del *
+ *****************************************************************************/
+
+static int
+cmd_keystore_del(int argc, char **argv)
+{
+ union ble_store_key key;
+ int obj_type;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = cmd_keystore_parse_keydata(argc, argv, &key, &obj_type);
+
+ if (rc) {
+ return rc;
+ }
+ rc = ble_store_delete(obj_type, &key);
+ return rc;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param keystore_del_params[] = {
+ {"type", "entry type, usage: =<msec|ssec|cccd>"},
+ {"addr_type", "usage: =<public|random>"},
+ {"addr", "usage: =<XX:XX:XX:XX:XX:XX>"},
+ {"ediv", "usage: =<UINT16>"},
+ {"rand", "usage: =<UINT64>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help keystore_del_help = {
+ .summary = "remove data from keystore",
+ .usage = NULL,
+ .params = keystore_del_params,
+};
+#endif
+
+/*****************************************************************************
+ * keystore-show *
+ *****************************************************************************/
+
+static int
+cmd_keystore_iterator(int obj_type,
+ union ble_store_value *val,
+ void *cookie) {
+
+ switch (obj_type) {
+ case BLE_STORE_OBJ_TYPE_PEER_SEC:
+ case BLE_STORE_OBJ_TYPE_OUR_SEC:
+ console_printf("Key: ");
+ if (ble_addr_cmp(&val->sec.peer_addr, BLE_ADDR_ANY) == 0) {
+ console_printf("ediv=%u ", val->sec.ediv);
+ console_printf("ediv=%llu ", val->sec.rand_num);
+ } else {
+ console_printf("addr_type=%u ", val->sec.peer_addr.type);
+ print_addr(val->sec.peer_addr.val);
+ }
+ console_printf("\n");
+
+ if (val->sec.ltk_present) {
+ console_printf(" LTK: ");
+ print_bytes(val->sec.ltk, 16);
+ console_printf("\n");
+ }
+ if (val->sec.irk_present) {
+ console_printf(" IRK: ");
+ print_bytes(val->sec.irk, 16);
+ console_printf("\n");
+ }
+ if (val->sec.csrk_present) {
+ console_printf(" CSRK: ");
+ print_bytes(val->sec.csrk, 16);
+ console_printf("\n");
+ }
+ break;
+ case BLE_STORE_OBJ_TYPE_CCCD:
+ console_printf("Key: ");
+ console_printf("addr_type=%u ", val->cccd.peer_addr.type);
+ print_addr(val->cccd.peer_addr.val);
+ console_printf("\n");
+
+ console_printf(" char_val_handle: %d\n", val->cccd.chr_val_handle);
+ console_printf(" flags: 0x%02x\n", val->cccd.flags);
+ console_printf(" changed: %d\n", val->cccd.value_changed);
+ break;
+ }
+ return 0;
+}
+
+static int
+cmd_keystore_show(int argc, char **argv)
+{
+ int type;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ type = parse_arg_kv("type", cmd_keystore_entry_type, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'type' parameter\n");
+ return rc;
+ }
+
+ ble_store_iterate(type, &cmd_keystore_iterator, NULL);
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param keystore_show_params[] = {
+ {"type", "entry type, usage: =<msec|ssec|cccd>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help keystore_show_help = {
+ .summary = "show data in keystore",
+ .usage = NULL,
+ .params = keystore_show_params,
+};
+#endif
+
+#if NIMBLE_BLE_SM
+/*****************************************************************************
+ * $show-oob-sc *
+ *****************************************************************************/
+
+extern struct ble_sm_sc_oob_data oob_data_local;
+extern struct ble_sm_sc_oob_data oob_data_remote;
+
+static int
+cmd_show_oob_sc(int argc, char **argv)
+{
+ console_printf("Local OOB Data: r=");
+ print_bytes(oob_data_local.r, 16);
+ console_printf(" c=");
+ print_bytes(oob_data_local.c, 16);
+ console_printf("\n");
+
+ console_printf("Remote OOB Data: r=");
+ print_bytes(oob_data_remote.r, 16);
+ console_printf(" c=");
+ print_bytes(oob_data_remote.c, 16);
+ console_printf("\n");
+
+ return 0;
+}
+
+/*****************************************************************************
+ * $auth-passkey *
+ *****************************************************************************/
+
+static int
+cmd_auth_passkey(int argc, char **argv)
+{
+ uint16_t conn_handle;
+ struct ble_sm_io pk;
+ char *yesno;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ pk.action = parse_arg_uint16("action", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'action' parameter\n");
+ return rc;
+ }
+
+ switch (pk.action) {
+ case BLE_SM_IOACT_INPUT:
+ case BLE_SM_IOACT_DISP:
+ /* passkey is 6 digit number */
+ pk.passkey = parse_arg_long_bounds("key", 0, 999999, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'key' parameter\n");
+ return rc;
+ }
+ break;
+
+ case BLE_SM_IOACT_OOB:
+ rc = parse_arg_byte_stream_exact_length("oob", pk.oob, 16);
+ if (rc != 0) {
+ console_printf("invalid 'oob' parameter\n");
+ return rc;
+ }
+ break;
+
+ case BLE_SM_IOACT_NUMCMP:
+ yesno = parse_arg_extract("yesno");
+ if (yesno == NULL) {
+ console_printf("invalid 'yesno' parameter\n");
+ return EINVAL;
+ }
+
+ switch (yesno[0]) {
+ case 'y':
+ case 'Y':
+ pk.numcmp_accept = 1;
+ break;
+ case 'n':
+ case 'N':
+ pk.numcmp_accept = 0;
+ break;
+
+ default:
+ console_printf("invalid 'yesno' parameter\n");
+ return EINVAL;
+ }
+ break;
+
+ case BLE_SM_IOACT_OOB_SC:
+ rc = parse_arg_byte_stream_exact_length("r", oob_data_remote.r, 16);
+ if (rc != 0 && rc != ENOENT) {
+ console_printf("invalid 'r' parameter\n");
+ return rc;
+ }
+
+ rc = parse_arg_byte_stream_exact_length("c", oob_data_remote.c, 16);
+ if (rc != 0 && rc != ENOENT) {
+ console_printf("invalid 'c' parameter\n");
+ return rc;
+ }
+ pk.oob_sc_data.local = &oob_data_local;
+ if (ble_hs_cfg.sm_oob_data_flag) {
+ pk.oob_sc_data.remote = &oob_data_remote;
+ } else {
+ pk.oob_sc_data.remote = NULL;
+ }
+ break;
+
+ default:
+ console_printf("invalid passkey action action=%d\n", pk.action);
+ return EINVAL;
+ }
+
+ rc = ble_sm_inject_io(conn_handle, &pk);
+ if (rc != 0) {
+ console_printf("error providing passkey; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param auth_passkey_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"action", "auth action type, usage: =<UINT16>"},
+ {"key", "usage: =[0-999999]"},
+ {"oob", "usage: =[XX:XX...], len=16 octets"},
+ {"yesno", "usage: =[string]"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help auth_passkey_help = {
+ .summary = "set authorization passkey options",
+ .usage = NULL,
+ .params = auth_passkey_params,
+};
+#endif
+
+/*****************************************************************************
+ * $security-pair *
+ *****************************************************************************/
+
+static int
+cmd_security_pair(int argc, char **argv)
+{
+ uint16_t conn_handle;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_sec_pair(conn_handle);
+ if (rc != 0) {
+ console_printf("error initiating pairing; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param security_pair_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help security_pair_help = {
+ .summary = "start pairing procedure for connection",
+ .usage = NULL,
+ .params = security_pair_params,
+};
+#endif
+
+/*****************************************************************************
+ * $security-unpair *
+ *****************************************************************************/
+
+static int
+cmd_security_unpair(int argc, char **argv)
+{
+ ble_addr_t peer;
+ int rc;
+ int oldest;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = parse_arg_bool_dflt("oldest", 0, &oldest);
+ if (rc != 0) {
+ console_printf("invalid 'oldest' parameter\n");
+ return rc;
+ }
+
+ if (oldest) {
+ rc = ble_gap_unpair_oldest_peer();
+ console_printf("Unpair oldest status: 0x%02x\n", rc);
+ return 0;
+ }
+
+ rc = parse_dev_addr("peer_", cmd_peer_addr_types, &peer);
+ if (rc != 0) {
+ console_printf("invalid 'peer_addr' parameter\n");
+ return rc;
+ }
+
+ rc = ble_gap_unpair(&peer);
+ if (rc != 0) {
+ console_printf("error unpairing; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param security_unpair_params[] = {
+ {"oldest", "usage: =[true|false], default: false"},
+ {"peer_addr_type", "usage: =[public|random|public_id|random_id], default: public"},
+ {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help security_unpair_help = {
+ .summary = "unpair a peer device",
+ .usage = NULL,
+ .params = security_unpair_params,
+};
+#endif
+
+/*****************************************************************************
+ * $security-start *
+ *****************************************************************************/
+
+static int
+cmd_security_start(int argc, char **argv)
+{
+ uint16_t conn_handle;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_sec_start(conn_handle);
+ if (rc != 0) {
+ console_printf("error starting security; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param security_start_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help security_start_help = {
+ .summary = "start security procedure for connection",
+ .usage = NULL,
+ .params = security_start_params,
+};
+#endif
+
+/*****************************************************************************
+ * $security-encryption *
+ *****************************************************************************/
+
+static int
+cmd_security_encryption(int argc, char **argv)
+{
+ uint16_t conn_handle;
+ uint16_t ediv;
+ uint64_t rand_val;
+ uint8_t ltk[16];
+ uint8_t key_size;
+ int rc;
+ int auth;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ ediv = parse_arg_uint16("ediv", &rc);
+ if (rc == ENOENT) {
+ rc = btshell_sec_restart(conn_handle, 0, NULL, 0, 0, 0);
+ } else {
+ rand_val = parse_arg_uint64("rand", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'rand' parameter\n");
+ return rc;
+ }
+
+ auth = parse_arg_bool("auth", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'auth' parameter\n");
+ return rc;
+ }
+
+ key_size = parse_arg_uint8("key_size", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'key_size' parameter\n");
+ return rc;
+ }
+
+ rc = parse_arg_byte_stream_exact_length("ltk", ltk, 16);
+ if (rc != 0) {
+ console_printf("invalid 'ltk' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_sec_restart(conn_handle, key_size,
+ ltk, ediv, rand_val, auth);
+ }
+
+ if (rc != 0) {
+ console_printf("error initiating encryption; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param security_encryption_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"ediv", "usage: =[UINT16]"},
+ {"rand", "usage: =[UINT64]"},
+ {"auth", "usage: =[0-1]"},
+ {"ltk", "usage: =[XX:XX...], len=16 octets"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help security_encryption_help = {
+ .summary = "start encryption procedure for connection",
+ .usage = NULL,
+ .params = security_encryption_params,
+};
+#endif
+
+/*****************************************************************************
+ * $security-set-data *
+ *****************************************************************************/
+
+static int
+cmd_security_set_data(int argc, char **argv)
+{
+ uint8_t tmp;
+ int good;
+ int rc;
+
+ good = 0;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ tmp = parse_arg_bool("oob_flag", &rc);
+ if (rc == 0) {
+ ble_hs_cfg.sm_oob_data_flag = tmp;
+ good++;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'oob_flag' parameter\n");
+ return rc;
+ }
+
+ tmp = parse_arg_bool("mitm_flag", &rc);
+ if (rc == 0) {
+ good++;
+ ble_hs_cfg.sm_mitm = tmp;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'mitm_flag' parameter\n");
+ return rc;
+ }
+
+ tmp = parse_arg_uint8("io_capabilities", &rc);
+ if (rc == 0) {
+ good++;
+ ble_hs_cfg.sm_io_cap = tmp;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'io_capabilities' parameter\n");
+ return rc;
+ }
+
+ tmp = parse_arg_uint8("our_key_dist", &rc);
+ if (rc == 0) {
+ good++;
+ ble_hs_cfg.sm_our_key_dist = tmp;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'our_key_dist' parameter\n");
+ return rc;
+ }
+
+ tmp = parse_arg_uint8("their_key_dist", &rc);
+ if (rc == 0) {
+ good++;
+ ble_hs_cfg.sm_their_key_dist = tmp;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'their_key_dist' parameter\n");
+ return rc;
+ }
+
+ tmp = parse_arg_bool("bonding", &rc);
+ if (rc == 0) {
+ good++;
+ ble_hs_cfg.sm_bonding = tmp;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'bonding' parameter\n");
+ return rc;
+ }
+
+ tmp = parse_arg_bool("sc", &rc);
+ if (rc == 0) {
+ good++;
+ ble_hs_cfg.sm_sc = tmp;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'sc' parameter\n");
+ return rc;
+ }
+
+ if (!good) {
+ console_printf("Error: no valid settings specified\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param security_set_data_params[] = {
+ {"oob_flag", "usage: =[0-1]"},
+ {"mitm_flag", "usage: =[0-1]"},
+ {"io_capabilities", "usage: =[UINT8]"},
+ {"our_key_dist", "usage: =[UINT8]"},
+ {"their_key_dist", "usage: =[UINT8]"},
+ {"bonding", "usage: =[0-1]"},
+ {"sc", "usage: =[0-1]"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help security_set_data_help = {
+ .summary = "set security data",
+ .usage = NULL,
+ .params = security_set_data_params,
+};
+#endif
+#endif
+
+/*****************************************************************************
+ * $test-tx *
+ * *
+ * Command to transmit 'num' packets of size 'len' at rate 'r' to
+ * handle 'h' Note that length must be <= 251. The rate is in msecs.
+ *
+ *****************************************************************************/
+
+static int
+cmd_test_tx(int argc, char **argv)
+{
+ int rc;
+ uint16_t conn;
+ uint16_t len;
+ uint16_t rate;
+ uint16_t num;
+ uint8_t stop;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ stop = parse_arg_uint8_dflt("stop", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'stop' parameter\n");
+ return rc;
+ }
+
+ if (stop) {
+ btshell_tx_stop();
+ return 0;
+ }
+
+ conn = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ len = parse_arg_uint16("length", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'length' parameter\n");
+ return rc;
+ }
+ if ((len > 251) || (len < 4)) {
+ console_printf("error: len must be between 4 and 251, inclusive");
+ }
+
+ rate = parse_arg_uint16_dflt("rate", 1, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'rate' parameter\n");
+ return rc;
+ }
+
+ num = parse_arg_uint16_dflt("num", 1, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'num' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_tx_start(conn, len, rate, num);
+ return rc;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param test_tx_params[] = {
+ {"conn", "handle to tx to, usage: =<UINT16>"},
+ {"length", "size of packet, usage: =<UINT16>"},
+ {"rate", "rate of tx, usage: =<UINT16>, default=1"},
+ {"num", "number of packets, usage: =<UINT16>, default=1"},
+ {"stop", "stop sending, usage: 1 to stop, default 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help test_tx_help = {
+ .summary = "test packet transmission",
+ .usage = NULL,
+ .params = test_tx_params,
+};
+#endif
+
+/*****************************************************************************
+ * $phy-set *
+ *****************************************************************************/
+
+static int
+cmd_phy_set(int argc, char **argv)
+{
+ uint16_t conn;
+ uint8_t tx_phys_mask;
+ uint8_t rx_phys_mask;
+ uint16_t phy_opts;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ tx_phys_mask = parse_arg_uint8("tx_phys_mask", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'tx_phys_mask' parameter\n");
+ return rc;
+ }
+
+ rx_phys_mask = parse_arg_uint8("rx_phys_mask", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'rx_phys_mask' parameter\n");
+ return rc;
+ }
+
+ phy_opts = parse_arg_uint16("phy_opts", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'phy_opts' parameter\n");
+ return rc;
+ }
+
+ return ble_gap_set_prefered_le_phy(conn, tx_phys_mask, rx_phys_mask,
+ phy_opts);
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param phy_set_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"tx_phys_mask", "usage: =<UINT8>"},
+ {"rx_phys_mask", "usage: =<UINT8>"},
+ {"phy_opts", "usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help phy_set_help = {
+ .summary = "set preferred PHYs",
+ .usage = NULL,
+ .params = phy_set_params,
+};
+#endif
+
+/*****************************************************************************
+ * $phy-set-default *
+ *****************************************************************************/
+
+static int
+cmd_phy_set_default(int argc, char **argv)
+{
+ uint8_t tx_phys_mask;
+ uint8_t rx_phys_mask;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ tx_phys_mask = parse_arg_uint8("tx_phys_mask", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'tx_phys_mask' parameter\n");
+ return rc;
+ }
+
+ rx_phys_mask = parse_arg_uint8("rx_phys_mask", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'rx_phys_mask' parameter\n");
+ return rc;
+ }
+
+ return ble_gap_set_prefered_default_le_phy(tx_phys_mask, rx_phys_mask);
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param phy_set_default_params[] = {
+ {"tx_phys_mask", "usage: =<UINT8>"},
+ {"rx_phys_mask", "usage: =<UINT8>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help phy_set_default_help = {
+ .summary = "set preferred default PHYs",
+ .usage = NULL,
+ .params = phy_set_default_params,
+};
+#endif
+
+/*****************************************************************************
+ * $phy-read *
+ *****************************************************************************/
+
+static int
+cmd_phy_read(int argc, char **argv)
+{
+ uint16_t conn = 0;
+ uint8_t tx_phy;
+ uint8_t rx_phy;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ rc = ble_gap_read_le_phy(conn, &tx_phy, &rx_phy);
+ if (rc != 0) {
+ console_printf("Could not read PHY error: %d\n", rc);
+ return rc;
+ }
+
+ console_printf("TX_PHY: %d\n", tx_phy);
+ console_printf("RX_PHY: %d\n", tx_phy);
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param phy_read_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help phy_read_help = {
+ .summary = "read PHYs",
+ .usage = NULL,
+ .params = phy_read_params,
+};
+#endif
+
+/*****************************************************************************
+ * $host-enable *
+ *****************************************************************************/
+
+static int
+cmd_host_enable(int argc, char **argv)
+{
+ int rc;
+
+ rc = gatt_svr_init();
+ assert(rc == 0);
+
+ ble_hs_sched_start();
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_cmd_help host_enable_help = {
+ .summary = "start the NimBLE host",
+ .usage = NULL,
+ .params = NULL,
+};
+#endif
+
+/*****************************************************************************
+ * $host-disable *
+ *****************************************************************************/
+
+static void
+on_stop(int status, void *arg)
+{
+ if (status == 0) {
+ console_printf("host stopped\n");
+ } else {
+ console_printf("host failed to stop; rc=%d\n", status);
+ }
+}
+
+static int
+cmd_host_disable(int argc, char **argv)
+{
+ static struct ble_hs_stop_listener listener;
+ int rc;
+
+ rc = ble_hs_stop(&listener, on_stop, NULL);
+ if (rc) {
+ return rc;
+ }
+
+ ble_gatts_reset();
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_cmd_help host_disable_help = {
+ .summary = "stop the NimBLE host",
+ .usage = NULL,
+ .params = NULL,
+};
+
+/*****************************************************************************
+ * $gatt-discover *
+ *****************************************************************************/
+
+static const struct shell_param gatt_discover_characteristic_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"uuid", "discover by uuid, usage: =[UUID]"},
+ {"start", "start handle, usage: =<UINT16>"},
+ {"end", "end handle, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help gatt_discover_characteristic_help = {
+ .summary = "perform characteristic discovery procedure",
+ .usage = NULL,
+ .params = gatt_discover_characteristic_params,
+};
+
+static const struct shell_param gatt_discover_descriptor_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"start", "start handle, usage: =<UINT16>"},
+ {"end", "end handle, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help gatt_discover_descriptor_help = {
+ .summary = "perform descriptor discovery procedure",
+ .usage = NULL,
+ .params = gatt_discover_descriptor_params,
+};
+
+static const struct shell_param gatt_discover_service_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"uuid", "discover by uuid, usage: =[UUID]"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help gatt_discover_service_help = {
+ .summary = "perform service discovery procedure",
+ .usage = NULL,
+ .params = gatt_discover_service_params,
+};
+
+static const struct shell_param gatt_discover_full_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help gatt_discover_full_help = {
+ .summary = "perform full discovery procedure",
+ .usage = NULL,
+ .params = gatt_discover_full_params,
+};
+
+/*****************************************************************************
+ * $gatt-exchange-mtu *
+ *****************************************************************************/
+
+static const struct shell_param gatt_exchange_mtu_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help gatt_exchange_mtu_help = {
+ .summary = "perform mtu exchange procedure",
+ .usage = NULL,
+ .params = gatt_exchange_mtu_params,
+};
+
+/*****************************************************************************
+ * $gatt-find-included-services *
+ *****************************************************************************/
+
+static const struct shell_param gatt_find_included_services_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"start", "start handle, usage: =<UINT16>"},
+ {"end", "end handle, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help gatt_find_included_services_help = {
+ .summary = "perform find included services procedure",
+ .usage = NULL,
+ .params = gatt_find_included_services_params,
+};
+
+/*****************************************************************************
+ * $gatt-notify *
+ *****************************************************************************/
+
+static const struct shell_param gatt_notify_params[] = {
+ {"attr", "attribute handle, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help gatt_notify_help = {
+ .summary = "notify about attribute value changed",
+ .usage = NULL,
+ .params = gatt_notify_params,
+};
+
+/*****************************************************************************
+ * $gatt-read *
+ *****************************************************************************/
+
+static const struct shell_param gatt_read_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"long", "is read long, usage: =[0-1], default=0"},
+ {"attr", "attribute handle, usage: =<UINT16>"},
+ {"offset", "offset value, usage: =<UINT16>"},
+ {"uuid", "read by uuid, usage: =[UUID]"},
+ {"start", "start handle, usage: =<UINT16>"},
+ {"end", "end handle, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help gatt_read_help = {
+ .summary = "perform gatt read procedure",
+ .usage = NULL,
+ .params = gatt_read_params,
+};
+
+/*****************************************************************************
+ * $gatt-service-changed *
+ *****************************************************************************/
+
+static const struct shell_param gatt_service_changed_params[] = {
+ {"start", "start handle, usage: =<UINT16>"},
+ {"end", "end handle, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help gatt_service_changed_help = {
+ .summary = "send service changed indication",
+ .usage = NULL,
+ .params = gatt_service_changed_params,
+};
+
+/*****************************************************************************
+ * $gatt-service-visibility *
+ *****************************************************************************/
+
+static const struct shell_param gatt_service_visibility_params[] = {
+ {"handle", "usage: =<UINT16>"},
+ {"visibility", "usage: =<0-1>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help gatt_service_visibility_help = {
+ .summary = "change service visibility",
+ .usage = NULL,
+ .params = gatt_service_visibility_params,
+};
+
+/*****************************************************************************
+ * $gatt-show *
+ *****************************************************************************/
+
+static const struct shell_param gatt_show_params[] = {
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help gatt_show_help = {
+ .summary = "show discovered gatt database",
+ .usage = NULL,
+ .params = gatt_show_params,
+};
+
+static const struct shell_cmd_help gatt_show_local_help = {
+ .summary = "show local gatt database",
+ .usage = NULL,
+ .params = gatt_show_params,
+};
+
+static const struct shell_cmd_help gatt_show_addr_help = {
+ .summary = "show device address",
+ .usage = NULL,
+ .params = gatt_show_params,
+};
+
+static const struct shell_cmd_help gatt_show_conn_help = {
+ .summary = "show connections information",
+ .usage = NULL,
+ .params = gatt_show_params,
+};
+
+/*****************************************************************************
+ * $gatt-write *
+ *****************************************************************************/
+
+static const struct shell_param gatt_write_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"no_rsp", "write without response, usage: =[0-1], default=0"},
+ {"long", "is write long, usage: =[0-1], default=0"},
+ {"attr", "attribute handle, usage: =<UINT16>"},
+ {"offset", "attribute handle, usage: =<UINT16>"},
+ {"value", "usage: =<octets>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help gatt_write_help = {
+ .summary = "perform gatt write procedure",
+ .usage = NULL,
+ .params = gatt_write_params,
+};
+#endif
+
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+/*****************************************************************************
+ * $l2cap-update *
+ *****************************************************************************/
+
+static const struct shell_param l2cap_update_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"interval_min", "usage: =[0-UINT16_MAX], default: 30"},
+ {"interval_max", "usage: =[0-UINT16_MAX], default: 50"},
+ {"latency", "usage: =[UINT16], default: 0"},
+ {"timeout", "usage: =[UINT16], default: 0x0100"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help l2cap_update_help = {
+ .summary = "update l2cap parameters for connection",
+ .usage = NULL,
+ .params = l2cap_update_params,
+};
+
+/*****************************************************************************
+ * $l2cap-create-server *
+ *****************************************************************************/
+
+static const struct shell_param l2cap_create_server_params[] = {
+ {"psm", "usage: =<UINT16>"},
+ {"mtu", "usage: =<UINT16> not more than BTSHELL_COC_MTU, default BTSHELL_COC_MTU"},
+ {"error", "usage: used for PTS testing:"},
+ {"", "0 - always accept"},
+ {"", "1 - reject with insufficient authentication"},
+ {"", "2 - reject with insufficient authorization"},
+ {"", "3 - reject with insufficient key size"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help l2cap_create_server_help = {
+ .summary = "create l2cap server",
+ .usage = NULL,
+ .params = l2cap_create_server_params,
+};
+
+/*****************************************************************************
+ * $l2cap-connect *
+ *****************************************************************************/
+
+static const struct shell_param l2cap_connect_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"psm", "usage: =<UINT16>"},
+ {"num", "usage: number of connection created in a row: [1-5]"},
+ {"mtu", "usage: =<UINT16> not more than BTSHELL_COC_MTU, default BTSHELL_COC_MTU"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help l2cap_connect_help = {
+ .summary = "perform l2cap connect procedure",
+ .usage = NULL,
+ .params = l2cap_connect_params,
+};
+
+/*****************************************************************************
+ * $l2cap-disconnect *
+ *****************************************************************************/
+
+static const struct shell_param l2cap_disconnect_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"idx", "usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help l2cap_disconnect_help = {
+ .summary = "perform l2cap disconnect procedure",
+ .usage = "use gatt-show-coc to get the parameters",
+ .params = l2cap_disconnect_params,
+};
+
+/*****************************************************************************
+ * $l2cap-reconfig *
+ *****************************************************************************/
+
+static const struct shell_param l2cap_reconfig_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"mtu", "new mtu, usage: =<UINT16>, default: 0 (no change)"},
+ {"idxs", "list of channel indexes, usage: idxs=1,3"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help l2cap_reconfig_help = {
+ .summary = "perform l2cap reconfigure procedure",
+ .usage = "use gatt-show-coc to get the parameters",
+ .params = l2cap_reconfig_params,
+};
+
+/*****************************************************************************
+ * $l2cap-send *
+ *****************************************************************************/
+
+static const struct shell_param l2cap_send_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"idx", "usage: =<UINT16>"},
+ {"bytes", "number of bytes to send, usage: =<UINT16>"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help l2cap_send_help = {
+ .summary = "perform l2cap send procedure",
+ .usage = "use l2cap-show-coc to get the parameters",
+ .params = l2cap_send_params,
+};
+
+/*****************************************************************************
+ * $l2cap-show-coc *
+ *****************************************************************************/
+
+static const struct shell_param l2cap_show_coc_params[] = {
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help l2cap_show_coc_help = {
+ .summary = "show coc information",
+ .usage = NULL,
+ .params = l2cap_show_coc_params,
+};
+
+#endif
+#endif
+
+#if MYNEWT_VAL(BLE_PERIODIC_ADV)
+static int
+cmd_periodic_configure(int argc, char **argv)
+{
+ struct ble_gap_periodic_adv_params params = {0};
+ uint8_t instance;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ instance = parse_arg_uint8_dflt("instance", 0, &rc);
+ if (rc != 0 || instance >= BLE_ADV_INSTANCES) {
+ console_printf("invalid instance\n");
+ return rc;
+ }
+
+ memset(&params, 0, sizeof(params));
+
+ params.include_tx_power = parse_arg_bool_dflt("include_tx_power", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'include_tx_power' parameter\n");
+ return rc;
+ }
+
+ params.itvl_min = parse_arg_time_dflt("interval_min", 1250, 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'interval_min' parameter\n");
+ return rc;
+ }
+
+ params.itvl_max = parse_arg_time_dflt("interval_max", 1250, params.itvl_min,
+ &rc);
+ if (rc != 0) {
+ console_printf("invalid 'interval_max' parameter\n");
+ return rc;
+ }
+
+ rc = ble_gap_periodic_adv_configure(instance, &params);
+ if (rc) {
+ console_printf("failed to configure periodic advertising\n");
+ return rc;
+ }
+
+ console_printf("Instance %u configured for periodic advertising\n",
+ instance);
+
+ return 0;
+}
+
+static int
+cmd_periodic_set_adv_data(int argc, char **argv)
+{
+ return cmd_set_adv_data_or_scan_rsp(argc, argv, false, true);
+}
+
+static int
+cmd_periodic_start(int argc, char **argv)
+{
+ uint8_t instance;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ instance = parse_arg_uint8_dflt("instance", 0, &rc);
+ if (rc != 0 || instance >= BLE_ADV_INSTANCES) {
+ console_printf("invalid instance\n");
+ return rc;
+ }
+
+ rc = ble_gap_periodic_adv_start(instance);
+ if (rc) {
+ console_printf("failed to start periodic advertising\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+cmd_periodic_stop(int argc, char **argv)
+{
+ uint8_t instance;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ instance = parse_arg_uint8_dflt("instance", 0, &rc);
+ if (rc != 0 || instance >= BLE_ADV_INSTANCES) {
+ console_printf("invalid instance\n");
+ return rc;
+ }
+
+ rc = ble_gap_periodic_adv_stop(instance);
+ if (rc) {
+ console_printf("failed to stop periodic advertising\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param periodic_configure_params[] = {
+ {"instance", "default: 0"},
+ {"interval_min", "usage: =[0-UINT32_MAX], default: 0"},
+ {"interval_max", "usage: =[0-UINT32_MAX], default: interval_min"},
+ {"tx_power", "include TX power, usage: =[0-1], default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help periodic_configure_help = {
+ .summary = "configure periodic advertising for instance",
+ .usage = NULL,
+ .params = periodic_configure_params,
+};
+
+static const struct shell_param periodic_start_params[] = {
+ {"instance", "default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help periodic_start_help = {
+ .summary = "start periodic advertising for instance",
+ .usage = NULL,
+ .params = periodic_start_params,
+};
+
+static const struct shell_param periodic_stop_params[] = {
+ {"instance", "default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help periodic_stop_help = {
+ .summary = "stop periodic advertising for instance",
+ .usage = NULL,
+ .params = periodic_stop_params,
+};
+#endif
+
+static int
+cmd_sync_create(int argc, char **argv)
+{
+ struct ble_gap_periodic_sync_params params = { 0 };
+ ble_addr_t addr;
+ ble_addr_t *addr_param = &addr;
+ uint8_t sid;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (argc > 1 && strcmp(argv[1], "cancel") == 0) {
+ rc = ble_gap_periodic_adv_sync_create_cancel();
+ if (rc != 0) {
+ console_printf("Sync create cancel fail: %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+ }
+
+ rc = parse_dev_addr("peer_", cmd_addr_type, &addr);
+ if (rc == ENOENT) {
+ /* With no "peer_addr" specified we'll use periodic list */
+ addr_param = NULL;
+ } else if (rc != 0) {
+ console_printf("invalid 'addr' parameter\n");
+ return rc;
+ }
+
+ sid = parse_arg_uint8_dflt("sid", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'sid' parameter\n");
+ return rc;
+ }
+
+ params.skip = parse_arg_uint16_dflt("skip", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'skip' parameter\n");
+ return rc;
+ }
+
+ params.sync_timeout = parse_arg_time_dflt("sync_timeout", 10000, 2000, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'sync_timeout' parameter\n");
+ return rc;
+ }
+
+ params.reports_disabled = parse_arg_bool_dflt("reports_disabled", false, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'reports_disabled' parameter\n");
+ return rc;
+ }
+
+ rc = ble_gap_periodic_adv_sync_create(addr_param, sid, &params,
+ btshell_gap_event, NULL);
+ if (rc) {
+ console_printf("Failed to create sync (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param sync_create_params[] = {
+ {"cancel", "cancel periodic sync establishment procedure"},
+ {"peer_addr_type", "usage: =[public|random], default: public"},
+ {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"},
+ {"sid", "usage: =[UINT8], default: 0"},
+ {"skip", "usage: =[0-0x01F3], default: 0x0000"},
+ {"sync_timeout", "usage: =[0x000A-0x4000], default: 0x07D0"},
+ {"reports_disabled", "disable reports, usage: =[0-1], default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help sync_create_help = {
+ .summary = "start/stop periodic sync procedure with specific parameters",
+ .usage = NULL,
+ .params = sync_create_params,
+};
+#endif
+
+#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER)
+static int
+cmd_sync_transfer(int argc, char **argv)
+{
+ uint16_t service_data;
+ uint16_t conn_handle;
+ uint16_t sync_handle;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ sync_handle = parse_arg_uint16_dflt("sync_handle", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'sync_handle' parameter\n");
+ return rc;
+ }
+
+ service_data = parse_arg_uint16_dflt("service_data", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'service_data' parameter\n");
+ return rc;
+ }
+
+ rc = ble_gap_periodic_adv_sync_transfer(sync_handle, conn_handle,
+ service_data);
+ if (rc) {
+ console_printf("Failed to transfer sync (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+static int
+cmd_sync_reporting(int argc, char **argv)
+{
+ uint16_t sync_handle;
+ bool enable;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ sync_handle = parse_arg_uint16_dflt("sync_handle", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'sync_handle' parameter\n");
+ return rc;
+ }
+
+ enable = parse_arg_bool_dflt("enabled", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'enabled' parameter\n");
+ return rc;
+ }
+
+ rc = ble_gap_periodic_adv_sync_reporting(sync_handle, enable);
+ if (rc) {
+ console_printf("Failed to %s reporting (%d)\n",
+ enable ? "enable" : "disable", rc);
+ }
+
+ return rc;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param sync_transfer_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"sync_handle", "sync handle, usage: =[UINT16], default: 0"},
+ {"service_data", "service data, usage: =[UINT16], default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help sync_transfer_help = {
+ .summary = "start periodic sync transfer procedure with specific parameters",
+ .usage = NULL,
+ .params = sync_transfer_params,
+};
+
+static const struct shell_param sync_reporting_params[] = {
+ {"sync_handle", "sync handle, usage: =[UINT16], default: 0"},
+ {"enabled", "toggle reporting, usage: =[0-1], default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help sync_reporting_help = {
+ .summary = "configure periodic advertising sync reporting",
+ .usage = NULL,
+ .params = sync_reporting_params,
+};
+#endif
+
+static int
+cmd_sync_transfer_set_info(int argc, char **argv)
+{
+ uint16_t service_data;
+ uint16_t conn_handle;
+ uint8_t instance;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ instance = parse_arg_uint8_dflt("instance", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'instance' parameter\n");
+ return rc;
+ }
+
+ service_data = parse_arg_uint16_dflt("service_data", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'service_data' parameter\n");
+ return rc;
+ }
+
+ rc = ble_gap_periodic_adv_sync_set_info(instance, conn_handle,
+ service_data);
+ if (rc) {
+ console_printf("Failed to transfer sync (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param sync_transfer_set_info_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"instance", "advertising instance, usage: =[UINT8], default: 0"},
+ {"service_data", "service data, usage: =[UINT16], default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help sync_transfer_set_info_help = {
+ .summary = "start periodic sync transfer procedure with specific parameters",
+ .usage = NULL,
+ .params = sync_transfer_set_info_params,
+};
+#endif
+
+static int
+cmd_sync_transfer_receive(int argc, char **argv)
+{
+ struct ble_gap_periodic_sync_params params = { 0 };
+ uint16_t conn_handle;
+ bool disable;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ disable = parse_arg_bool_dflt("disable", false, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'disable' parameter\n");
+ return rc;
+ }
+
+ if (disable) {
+ rc = ble_gap_periodic_adv_sync_receive(conn_handle, NULL, NULL, NULL);
+ if (rc) {
+ console_printf("Failed to disable sync transfer reception (%d)\n", rc);
+ }
+
+ return rc;
+ }
+
+ params.skip = parse_arg_uint16_dflt("skip", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'skip' parameter\n");
+ return rc;
+ }
+
+ params.sync_timeout = parse_arg_time_dflt("sync_timeout", 10000, 10, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'sync_timeout' parameter\n");
+ return rc;
+ }
+
+ params.reports_disabled = parse_arg_bool_dflt("reports_disabled", false, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'reports_disabled' parameter\n");
+ return rc;
+ }
+
+ rc = ble_gap_periodic_adv_sync_receive(conn_handle, &params, btshell_gap_event,
+ NULL);
+ if (rc) {
+ console_printf("Failed to enable sync transfer reception (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param sync_transfer_receive_params[] = {
+ {"conn", "connection handle, usage: =<UINT16>"},
+ {"disable", "disable transfer reception, usage: =[0-1], default: 0"},
+ {"skip", "usage: =[0-0x01F3], default: 0x0000"},
+ {"sync_timeout", "usage: =[0x000A-0x4000], default: 0x000A"},
+ {"reports_disabled", "disable reports, usage: =[0-1], default: 0"},
+ {NULL, NULL}
+};
+#endif
+
+static const struct shell_cmd_help sync_transfer_receive_help = {
+ .summary = "start/stop periodic sync reception with specific parameters",
+ .usage = NULL,
+ .params = sync_transfer_receive_params,
+};
+#endif
+
+static int
+cmd_sync_terminate(int argc, char **argv)
+{
+ uint16_t sync_handle;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ sync_handle = parse_arg_uint16_dflt("sync_handle", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'sync_handle' parameter\n");
+ return rc;
+ }
+
+ rc = ble_gap_periodic_adv_sync_terminate(sync_handle);
+ if (rc) {
+ console_printf("Failed to terminate sync (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param sync_terminate_params[] = {
+ {"sync_handle", "usage: =[UINT16], default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help sync_terminate_help = {
+ .summary = "terminate periodic sync",
+ .usage = NULL,
+ .params = sync_terminate_params,
+};
+#endif
+
+static int
+cmd_sync_stats(int argc, char **argv)
+{
+ uint16_t sync_handle;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ sync_handle = parse_arg_uint16_dflt("sync_handle", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'sync_handle' parameter\n");
+ return rc;
+ }
+
+ btshell_sync_stats(sync_handle);
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param sync_stats_params[] = {
+ {"sync_handle", "usage: =[UINT16], default: 0"},
+ {NULL, NULL}
+};
+
+static const struct shell_cmd_help sync_stats_help = {
+ .summary = "show sync stats",
+ .usage = NULL,
+ .params = sync_stats_params,
+};
+#endif
+#endif
+
+static const struct shell_cmd btshell_commands[] = {
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ {
+ .sc_cmd = "advertise-configure",
+ .sc_cmd_func = cmd_advertise_configure,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &advertise_configure_help,
+#endif
+ },
+ {
+ .sc_cmd = "advertise-set-addr",
+ .sc_cmd_func = cmd_advertise_set_addr,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &advertise_set_addr_help,
+#endif
+ },
+ {
+ .sc_cmd = "advertise-set-adv-data",
+ .sc_cmd_func = cmd_set_adv_data,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &set_adv_data_help,
+#endif
+ },
+ {
+ .sc_cmd = "advertise-set-scan-rsp",
+ .sc_cmd_func = cmd_set_scan_rsp,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &set_scan_rsp_help,
+#endif
+ },
+ {
+ .sc_cmd = "advertise-start",
+ .sc_cmd_func = cmd_advertise_start,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &advertise_start_help,
+#endif
+ },
+ {
+ .sc_cmd = "advertise-stop",
+ .sc_cmd_func = cmd_advertise_stop,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &advertise_stop_help,
+#endif
+ },
+ {
+ .sc_cmd = "advertise-remove",
+ .sc_cmd_func = cmd_advertise_remove,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &advertise_remove_help,
+#endif
+ },
+#else
+ {
+ .sc_cmd = "advertise",
+ .sc_cmd_func = cmd_advertise,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &advertise_help,
+#endif
+ },
+#endif
+ {
+ .sc_cmd = "connect",
+ .sc_cmd_func = cmd_connect,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &connect_help,
+#endif
+ },
+ {
+ .sc_cmd = "disconnect",
+ .sc_cmd_func = cmd_disconnect,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &disconnect_help,
+#endif
+ },
+ {
+ .sc_cmd = "show-addr",
+ .sc_cmd_func = cmd_show_addr,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_show_addr_help,
+#endif
+ },
+ {
+ .sc_cmd = "show-conn",
+ .sc_cmd_func = cmd_show_conn,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_show_conn_help,
+#endif
+ },
+ {
+ .sc_cmd = "set-scan-opts",
+ .sc_cmd_func = cmd_set_scan_opts,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &set_scan_opts_help,
+#endif
+ },
+ {
+ .sc_cmd = "scan",
+ .sc_cmd_func = cmd_scan,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &scan_help,
+#endif
+ },
+ {
+ .sc_cmd = "set",
+ .sc_cmd_func = cmd_set,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &set_help,
+#endif
+ },
+#if !MYNEWT_VAL(BLE_EXT_ADV)
+ {
+ .sc_cmd = "set-adv-data",
+ .sc_cmd_func = cmd_set_adv_data,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &set_adv_data_help,
+#endif
+ },
+ {
+ .sc_cmd = "set-scan-rsp",
+ .sc_cmd_func = cmd_set_scan_rsp,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &set_scan_rsp_help,
+#endif
+ },
+#endif
+ {
+ .sc_cmd = "set-priv-mode",
+ .sc_cmd_func = cmd_set_priv_mode,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &set_priv_mode_help,
+#endif
+ },
+ {
+ .sc_cmd = "white-list",
+ .sc_cmd_func = cmd_white_list,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &white_list_help,
+#endif
+ },
+ {
+ .sc_cmd = "conn-rssi",
+ .sc_cmd_func = cmd_conn_rssi,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &conn_rssi_help,
+#endif
+ },
+ {
+ .sc_cmd = "conn-update-params",
+ .sc_cmd_func = cmd_conn_update_params,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &conn_update_params_help,
+#endif
+ },
+ {
+ .sc_cmd = "conn-datalen",
+ .sc_cmd_func = cmd_conn_datalen,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &conn_datalen_help,
+#endif
+ },
+ {
+ .sc_cmd = "gatt-discover-characteristic",
+ .sc_cmd_func = cmd_gatt_discover_characteristic,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_discover_characteristic_help,
+#endif
+ },
+ {
+ .sc_cmd = "gatt-discover-descriptor",
+ .sc_cmd_func = cmd_gatt_discover_descriptor,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_discover_descriptor_help,
+#endif
+ },
+ {
+ .sc_cmd = "gatt-discover-service",
+ .sc_cmd_func = cmd_gatt_discover_service,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_discover_service_help,
+#endif
+ },
+ {
+ .sc_cmd = "gatt-discover-full",
+ .sc_cmd_func = cmd_gatt_discover_full,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_discover_full_help,
+#endif
+ },
+ {
+ .sc_cmd = "gatt-find-included-services",
+ .sc_cmd_func = cmd_gatt_find_included_services,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_find_included_services_help,
+#endif
+ },
+ {
+ .sc_cmd = "gatt-exchange-mtu",
+ .sc_cmd_func = cmd_gatt_exchange_mtu,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_exchange_mtu_help,
+#endif
+ },
+ {
+ .sc_cmd = "gatt-read",
+ .sc_cmd_func = cmd_gatt_read,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_read_help,
+#endif
+ },
+ {
+ .sc_cmd = "gatt-notify",
+ .sc_cmd_func = cmd_gatt_notify,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_notify_help,
+#endif
+ },
+ {
+ .sc_cmd = "gatt-service-changed",
+ .sc_cmd_func = cmd_gatt_service_changed,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_service_changed_help,
+#endif
+ },
+ {
+ .sc_cmd = "gatt-service-visibility",
+ .sc_cmd_func = cmd_gatt_service_visibility,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_service_visibility_help,
+#endif
+ },
+ {
+ .sc_cmd = "gatt-show",
+ .sc_cmd_func = cmd_gatt_show,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_show_help,
+#endif
+ },
+ {
+ .sc_cmd = "gatt-show-local",
+ .sc_cmd_func = cmd_gatt_show_local,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_show_local_help,
+#endif
+ },
+ {
+ .sc_cmd = "gatt-write",
+ .sc_cmd_func = cmd_gatt_write,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &gatt_write_help,
+#endif
+ },
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
+ {
+ .sc_cmd = "l2cap-update",
+ .sc_cmd_func = cmd_l2cap_update,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &l2cap_update_help,
+#endif
+ },
+ {
+ .sc_cmd = "l2cap-create-server",
+ .sc_cmd_func = cmd_l2cap_create_server,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &l2cap_create_server_help,
+#endif
+ },
+ {
+ .sc_cmd = "l2cap-connect",
+ .sc_cmd_func = cmd_l2cap_connect,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &l2cap_connect_help,
+#endif
+ },
+ {
+ .sc_cmd = "l2cap-reconfig",
+ .sc_cmd_func = cmd_l2cap_reconfig,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &l2cap_reconfig_help,
+#endif
+ },
+ {
+ .sc_cmd = "l2cap-disconnect",
+ .sc_cmd_func = cmd_l2cap_disconnect,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &l2cap_disconnect_help,
+#endif
+ },
+ {
+ .sc_cmd = "l2cap-send",
+ .sc_cmd_func = cmd_l2cap_send,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &l2cap_send_help,
+#endif
+ },
+ {
+ .sc_cmd = "l2cap-show-coc",
+ .sc_cmd_func = cmd_l2cap_show_coc,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &l2cap_show_coc_help,
+#endif
+ },
+#endif
+ {
+ .sc_cmd = "keystore-add",
+ .sc_cmd_func = cmd_keystore_add,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &keystore_add_help,
+#endif
+ },
+ {
+ .sc_cmd = "keystore-del",
+ .sc_cmd_func = cmd_keystore_del,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &keystore_del_help,
+#endif
+ },
+ {
+ .sc_cmd = "keystore-show",
+ .sc_cmd_func = cmd_keystore_show,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &keystore_show_help,
+#endif
+ },
+#if NIMBLE_BLE_SM
+ {
+ .sc_cmd = "show-oob-sc",
+ .sc_cmd_func = cmd_show_oob_sc,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = NULL,
+#endif
+ },
+ {
+ .sc_cmd = "auth-passkey",
+ .sc_cmd_func = cmd_auth_passkey,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &auth_passkey_help,
+#endif
+ },
+ {
+ .sc_cmd = "security-pair",
+ .sc_cmd_func = cmd_security_pair,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &security_pair_help,
+#endif
+ },
+ {
+ .sc_cmd = "security-unpair",
+ .sc_cmd_func = cmd_security_unpair,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &security_unpair_help,
+#endif
+ },
+ {
+ .sc_cmd = "security-start",
+ .sc_cmd_func = cmd_security_start,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &security_start_help,
+#endif
+ },
+ {
+ .sc_cmd = "security-encryption",
+ .sc_cmd_func = cmd_security_encryption,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &security_encryption_help,
+#endif
+ },
+ {
+ .sc_cmd = "security-set-data",
+ .sc_cmd_func = cmd_security_set_data,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &security_set_data_help,
+#endif
+ },
+#endif
+ {
+ .sc_cmd = "test-tx",
+ .sc_cmd_func = cmd_test_tx,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &test_tx_help,
+#endif
+ },
+ {
+ .sc_cmd = "phy-set",
+ .sc_cmd_func = cmd_phy_set,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &phy_set_help,
+#endif
+ },
+ {
+ .sc_cmd = "phy-set-default",
+ .sc_cmd_func = cmd_phy_set_default,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &phy_set_default_help,
+#endif
+ },
+ {
+ .sc_cmd = "phy-read",
+ .sc_cmd_func = cmd_phy_read,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &phy_read_help,
+#endif
+ },
+ {
+ .sc_cmd = "host-enable",
+ .sc_cmd_func = cmd_host_enable,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &host_enable_help,
+#endif
+ },
+ {
+ .sc_cmd = "host-disable",
+ .sc_cmd_func = cmd_host_disable,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &host_disable_help,
+#endif
+ },
+#if MYNEWT_VAL(BLE_PERIODIC_ADV)
+ {
+ .sc_cmd = "periodic-configure",
+ .sc_cmd_func = cmd_periodic_configure,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &periodic_configure_help,
+#endif
+ },
+ {
+ .sc_cmd = "periodic-set-adv-data",
+ .sc_cmd_func = cmd_periodic_set_adv_data,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &set_adv_data_help,
+#endif
+ },
+ {
+ .sc_cmd = "periodic-start",
+ .sc_cmd_func = cmd_periodic_start,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &periodic_start_help,
+#endif
+ },
+ {
+ .sc_cmd = "periodic-stop",
+ .sc_cmd_func = cmd_periodic_stop,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &periodic_stop_help,
+#endif
+ },
+ {
+ .sc_cmd = "sync-create",
+ .sc_cmd_func = cmd_sync_create,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &sync_create_help,
+#endif
+ },
+ {
+ .sc_cmd = "sync-terminate",
+ .sc_cmd_func = cmd_sync_terminate,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &sync_terminate_help,
+#endif
+ },
+ {
+ .sc_cmd = "sync-stats",
+ .sc_cmd_func = cmd_sync_stats,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &sync_stats_help,
+#endif
+ },
+#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER)
+ {
+ .sc_cmd = "sync-transfer",
+ .sc_cmd_func = cmd_sync_transfer,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &sync_transfer_help,
+#endif
+ },
+ {
+ .sc_cmd = "sync-transfer-set-info",
+ .sc_cmd_func = cmd_sync_transfer_set_info,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &sync_transfer_set_info_help,
+#endif
+ },
+ {
+ .sc_cmd = "sync-transfer-receive",
+ .sc_cmd_func = cmd_sync_transfer_receive,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &sync_transfer_receive_help,
+#endif
+ },
+ {
+ .sc_cmd = "sync-reporting",
+ .sc_cmd_func = cmd_sync_reporting,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &sync_reporting_help,
+#endif
+ },
+#endif
+#endif
+ { 0 },
+};
+
+
+void
+cmd_init(void)
+{
+ shell_register(BTSHELL_MODULE, btshell_commands);
+ shell_register_default_module(BTSHELL_MODULE);
+}
diff --git a/src/libs/mynewt-nimble/apps/btshell/src/cmd.h b/src/libs/mynewt-nimble/apps/btshell/src/cmd.h
new file mode 100644
index 00000000..63bd50d1
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/btshell/src/cmd.h
@@ -0,0 +1,68 @@
+/*
+ * 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 CMD_H
+#define CMD_H
+
+#include <inttypes.h>
+#include "host/ble_uuid.h"
+
+struct kv_pair {
+ char *key;
+ int val;
+};
+
+uint32_t parse_arg_time_dflt(char *name, int step, uint32_t dflt, int *out_status);
+const struct kv_pair *parse_kv_find(const struct kv_pair *kvs, char *name);
+int parse_arg_find_idx(const char *key);
+char *parse_arg_extract(const char *key);
+long parse_arg_long_bounds(char *name, long min, long max, int *out_status);
+long parse_arg_long_bounds_dflt(char *name, long min, long max,
+ long dflt, int *out_status);
+uint64_t parse_arg_uint64_bounds(char *name, uint64_t min,
+ uint64_t max, int *out_status);
+long parse_arg_long(char *name, int *staus);
+uint8_t parse_arg_bool(char *name, int *status);
+uint8_t parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status);
+uint8_t parse_arg_uint8(char *name, int *status);
+uint8_t parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status);
+uint16_t parse_arg_uint16(char *name, int *status);
+uint16_t parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status);
+uint32_t parse_arg_uint32(char *name, int *out_status);
+uint32_t parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status);
+uint64_t parse_arg_uint64(char *name, int *out_status);
+int parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status);
+int parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val,
+ int *out_status);
+int parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len);
+int parse_arg_uint8_list_with_separator(char *name, char *separator, int max_len,
+ uint8_t *dst, int *out_len);
+int parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len);
+int parse_arg_mac(char *name, uint8_t *dst);
+int parse_arg_addr(char *name, ble_addr_t *addr);
+int parse_arg_uuid(char *name, ble_uuid_any_t *uuid);
+int parse_arg_all(int argc, char **argv);
+int parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body,
+ uint8_t *out_body_len, uint8_t *out_suffix);
+int cmd_parse_conn_start_end(uint16_t *out_conn, uint16_t *out_start,
+ uint16_t *out_end);
+
+void cmd_init(void);
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.c b/src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.c
new file mode 100644
index 00000000..ba3799e5
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.c
@@ -0,0 +1,587 @@
+/*
+ * 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 <inttypes.h>
+#include <errno.h>
+
+#include "bsp/bsp.h"
+#include "host/ble_hs_mbuf.h"
+#include "host/ble_gap.h"
+#include "services/gatt/ble_svc_gatt.h"
+#include "console/console.h"
+#include "btshell.h"
+#include "cmd.h"
+#include "cmd_gatt.h"
+
+#define CMD_BUF_SZ 256
+static bssnz_t uint8_t cmd_buf[CMD_BUF_SZ];
+
+/*****************************************************************************
+ * $gatt-discover *
+ *****************************************************************************/
+
+int
+cmd_gatt_discover_characteristic(int argc, char **argv)
+{
+ uint16_t start_handle;
+ uint16_t conn_handle;
+ uint16_t end_handle;
+ ble_uuid_any_t uuid;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
+ if (rc != 0) {
+ console_printf("invalid 'conn start end' parameter\n");
+ return rc;
+ }
+
+ rc = parse_arg_uuid("uuid", &uuid);
+ if (rc == 0) {
+ rc = btshell_disc_chrs_by_uuid(conn_handle, start_handle, end_handle,
+ &uuid.u);
+ } else if (rc == ENOENT) {
+ rc = btshell_disc_all_chrs(conn_handle, start_handle, end_handle);
+ } else {
+ console_printf("invalid 'uuid' parameter\n");
+ return rc;
+ }
+ if (rc != 0) {
+ console_printf("error discovering characteristics; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+int
+cmd_gatt_discover_descriptor(int argc, char **argv)
+{
+ uint16_t start_handle;
+ uint16_t conn_handle;
+ uint16_t end_handle;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
+ if (rc != 0) {
+ console_printf("invalid 'conn start end' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_disc_all_dscs(conn_handle, start_handle, end_handle);
+ if (rc != 0) {
+ console_printf("error discovering descriptors; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+int
+cmd_gatt_discover_service(int argc, char **argv)
+{
+ ble_uuid_any_t uuid;
+ int conn_handle;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ rc = parse_arg_uuid("uuid", &uuid);
+ if (rc == 0) {
+ rc = btshell_disc_svc_by_uuid(conn_handle, &uuid.u);
+ } else if (rc == ENOENT) {
+ rc = btshell_disc_svcs(conn_handle);
+ } else {
+ console_printf("invalid 'uuid' parameter\n");
+ return rc;
+ }
+
+ if (rc != 0) {
+ console_printf("error discovering services; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+int
+cmd_gatt_discover_full(int argc, char **argv)
+{
+ int conn_handle;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_disc_full(conn_handle);
+ if (rc != 0) {
+ console_printf("error discovering all; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+ * $gatt-exchange-mtu *
+ *****************************************************************************/
+
+int
+cmd_gatt_exchange_mtu(int argc, char **argv)
+{
+ uint16_t conn_handle;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_exchange_mtu(conn_handle);
+ if (rc != 0) {
+ console_printf("error exchanging mtu; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+ * $gatt-notify *
+ *****************************************************************************/
+
+int
+cmd_gatt_notify(int argc, char **argv)
+{
+ uint16_t attr_handle;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ attr_handle = parse_arg_uint16("attr", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'attr' parameter\n");
+ return rc;
+ }
+
+ btshell_notify(attr_handle);
+
+ return 0;
+}
+
+/*****************************************************************************
+ * $gatt-read *
+ *****************************************************************************/
+
+#define CMD_READ_MAX_ATTRS 8
+
+int
+cmd_gatt_read(int argc, char **argv)
+{
+ static uint16_t attr_handles[CMD_READ_MAX_ATTRS];
+ uint16_t conn_handle;
+ uint16_t start;
+ uint16_t end;
+ uint16_t offset;
+ ble_uuid_any_t uuid;
+ uint8_t num_attr_handles;
+ int is_uuid;
+ int is_long;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ is_long = parse_arg_long("long", &rc);
+ if (rc == ENOENT) {
+ is_long = 0;
+ } else if (rc != 0) {
+ console_printf("invalid 'long' parameter\n");
+ return rc;
+ }
+
+ for (num_attr_handles = 0;
+ num_attr_handles < CMD_READ_MAX_ATTRS;
+ num_attr_handles++) {
+
+ attr_handles[num_attr_handles] = parse_arg_uint16("attr", &rc);
+ if (rc == ENOENT) {
+ break;
+ } else if (rc != 0) {
+ console_printf("invalid 'attr' parameter\n");
+ return rc;
+ }
+ }
+
+ rc = parse_arg_uuid("uuid", &uuid);
+ if (rc == ENOENT) {
+ is_uuid = 0;
+ } else if (rc == 0) {
+ is_uuid = 1;
+ } else {
+ console_printf("invalid 'uuid' parameter\n");
+ return rc;
+ }
+
+ start = parse_arg_uint16("start", &rc);
+ if (rc == ENOENT) {
+ start = 0;
+ } else if (rc != 0) {
+ console_printf("invalid 'start' parameter\n");
+ return rc;
+ }
+
+ end = parse_arg_uint16("end", &rc);
+ if (rc == ENOENT) {
+ end = 0;
+ } else if (rc != 0) {
+ console_printf("invalid 'end' parameter\n");
+ return rc;
+ }
+
+ offset = parse_arg_uint16("offset", &rc);
+ if (rc == ENOENT) {
+ offset = 0;
+ } else if (rc != 0) {
+ console_printf("invalid 'offset' parameter\n");
+ return rc;
+ }
+
+ if (num_attr_handles == 1) {
+ if (is_long) {
+ rc = btshell_read_long(conn_handle, attr_handles[0], offset);
+ } else {
+ rc = btshell_read(conn_handle, attr_handles[0]);
+ }
+ } else if (num_attr_handles > 1) {
+ rc = btshell_read_mult(conn_handle, attr_handles, num_attr_handles);
+ } else if (is_uuid) {
+ if (start == 0 || end == 0) {
+ rc = EINVAL;
+ } else {
+ rc = btshell_read_by_uuid(conn_handle, start, end, &uuid.u);
+ }
+ } else {
+ rc = EINVAL;
+ }
+
+ if (rc != 0) {
+ console_printf("error reading characteristic; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+
+/*****************************************************************************
+ * $gatt-service-changed *
+ *****************************************************************************/
+
+int
+cmd_gatt_service_changed(int argc, char **argv)
+{
+ uint16_t start;
+ uint16_t end;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ start = parse_arg_uint16("start", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'start' parameter\n");
+ return rc;
+ }
+
+ end = parse_arg_uint16("end", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'end' parameter\n");
+ return rc;
+ }
+
+ ble_svc_gatt_changed(start, end);
+
+ return 0;
+}
+
+/*****************************************************************************
+ * $gatt-service-visibility *
+ *****************************************************************************/
+
+int
+cmd_gatt_service_visibility(int argc, char **argv)
+{
+ uint16_t handle;
+ bool vis;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ handle = parse_arg_uint16("handle", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'handle' parameter\n");
+ return rc;
+ }
+
+ vis = parse_arg_bool("visibility", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'visibility' parameter\n");
+ return rc;
+ }
+
+ ble_gatts_svc_set_visibility(handle, vis);
+
+ return 0;
+}
+
+/*****************************************************************************
+ * $gatt-find-included-services *
+ *****************************************************************************/
+
+int
+cmd_gatt_find_included_services(int argc, char **argv)
+{
+ uint16_t start_handle;
+ uint16_t conn_handle;
+ uint16_t end_handle;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
+ if (rc != 0) {
+ console_printf("invalid 'conn start end' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_find_inc_svcs(conn_handle, start_handle, end_handle);
+ if (rc != 0) {
+ console_printf("error finding included services; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+ * $gatt-show *
+ *****************************************************************************/
+
+int
+cmd_gatt_show(int argc, char **argv)
+{
+ struct btshell_conn *conn;
+ struct btshell_svc *svc;
+ int i;
+
+ for (i = 0; i < btshell_num_conns; i++) {
+ conn = btshell_conns + i;
+
+ console_printf("CONNECTION: handle=%d\n", conn->handle);
+
+ SLIST_FOREACH(svc, &conn->svcs, next) {
+ print_svc(svc);
+ }
+ }
+
+ return 0;
+}
+
+int
+cmd_gatt_show_local(int argc, char **argv)
+{
+ gatt_svr_print_svcs();
+ return 0;
+}
+
+/*****************************************************************************
+ * $gatt-write *
+ *****************************************************************************/
+
+int
+cmd_gatt_write(int argc, char **argv)
+{
+ struct ble_gatt_attr attrs[MYNEWT_VAL(BLE_GATT_WRITE_MAX_ATTRS)] = { { 0 } };
+ uint16_t attr_handle;
+ uint16_t conn_handle;
+ uint16_t offset;
+ int total_attr_len;
+ int num_attrs;
+ int attr_len;
+ int is_long;
+ int no_rsp;
+ int rc;
+ int i;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ no_rsp = parse_arg_bool_dflt("no_rsp", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'no_rsp' parameter\n");
+ return rc;
+ }
+
+ is_long = parse_arg_bool_dflt("long", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'long' parameter\n");
+ return rc;
+ }
+
+ total_attr_len = 0;
+ num_attrs = 0;
+ while (1) {
+ attr_handle = parse_arg_uint16("attr", &rc);
+ if (rc == ENOENT) {
+ break;
+ } else if (rc != 0) {
+ rc = -rc;
+ console_printf("invalid 'attr' parameter\n");
+ goto done;
+ }
+
+ rc = parse_arg_byte_stream("value", sizeof cmd_buf - total_attr_len,
+ cmd_buf + total_attr_len, &attr_len);
+ if (rc == ENOENT) {
+ break;
+ } else if (rc != 0) {
+ console_printf("invalid 'value' parameter\n");
+ goto done;
+ }
+
+ offset = parse_arg_uint16("offset", &rc);
+ if (rc == ENOENT) {
+ offset = 0;
+ } else if (rc != 0) {
+ console_printf("invalid 'offset' parameter\n");
+ return rc;
+ }
+
+ if (num_attrs >= sizeof attrs / sizeof attrs[0]) {
+ rc = -EINVAL;
+ goto done;
+ }
+
+ attrs[num_attrs].handle = attr_handle;
+ attrs[num_attrs].offset = offset;
+ attrs[num_attrs].om = ble_hs_mbuf_from_flat(cmd_buf + total_attr_len,
+ attr_len);
+ if (attrs[num_attrs].om == NULL) {
+ goto done;
+ }
+
+ total_attr_len += attr_len;
+ num_attrs++;
+ }
+
+ if (no_rsp) {
+ if (num_attrs != 1) {
+ rc = -EINVAL;
+ goto done;
+ }
+ rc = btshell_write_no_rsp(conn_handle, attrs[0].handle, attrs[0].om);
+ attrs[0].om = NULL;
+ } else if (is_long) {
+ if (num_attrs != 1) {
+ rc = -EINVAL;
+ goto done;
+ }
+ rc = btshell_write_long(conn_handle, attrs[0].handle,
+ attrs[0].offset, attrs[0].om);
+ attrs[0].om = NULL;
+ } else if (num_attrs > 1) {
+ rc = btshell_write_reliable(conn_handle, attrs, num_attrs);
+ } else if (num_attrs == 1) {
+ rc = btshell_write(conn_handle, attrs[0].handle, attrs[0].om);
+ attrs[0].om = NULL;
+ } else {
+ rc = -EINVAL;
+ }
+
+done:
+ for (i = 0; i < sizeof attrs / sizeof attrs[0]; i++) {
+ os_mbuf_free_chain(attrs[i].om);
+ }
+
+ if (rc != 0) {
+ console_printf("error writing characteristic; rc=%d\n", rc);
+ }
+
+ return rc;
+}
diff --git a/src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.h b/src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.h
new file mode 100644
index 00000000..70536d03
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.h
@@ -0,0 +1,39 @@
+/*
+ * 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 CMD_GATT_H
+#define CMD_GATT_H
+
+#include "cmd.h"
+
+int cmd_gatt_discover_characteristic(int argc, char **argv);
+int cmd_gatt_discover_descriptor(int argc, char **argv);
+int cmd_gatt_discover_service(int argc, char **argv);
+int cmd_gatt_discover_full(int argc, char **argv);
+int cmd_gatt_find_included_services(int argc, char **argv);
+int cmd_gatt_exchange_mtu(int argc, char **argv);
+int cmd_gatt_notify(int argc, char **argv);
+int cmd_gatt_read(int argc, char **argv);
+int cmd_gatt_service_changed(int argc, char **argv);
+int cmd_gatt_service_visibility(int argc, char **argv);
+int cmd_gatt_show(int argc, char **argv);
+int cmd_gatt_show_local(int argc, char **argv);
+int cmd_gatt_write(int argc, char **argv);
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.c b/src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.c
new file mode 100644
index 00000000..e74e3bf3
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.c
@@ -0,0 +1,325 @@
+/*
+ * 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 <inttypes.h>
+#include <errno.h>
+
+#include "syscfg/syscfg.h"
+#include "host/ble_gap.h"
+#include "host/ble_l2cap.h"
+#include "console/console.h"
+#include "btshell.h"
+#include "cmd.h"
+#include "cmd_l2cap.h"
+
+
+/*****************************************************************************
+ * $l2cap-update *
+ *****************************************************************************/
+
+int
+cmd_l2cap_update(int argc, char **argv)
+{
+ struct ble_l2cap_sig_update_params params;
+ uint16_t conn_handle;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn_handle = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ params.itvl_min = parse_arg_uint16_dflt("interval_min",
+ BLE_GAP_INITIAL_CONN_ITVL_MIN,
+ &rc);
+ if (rc != 0) {
+ console_printf("invalid 'interval_min' parameter\n");
+ return rc;
+ }
+
+ params.itvl_max = parse_arg_uint16_dflt("interval_max",
+ BLE_GAP_INITIAL_CONN_ITVL_MAX,
+ &rc);
+ if (rc != 0) {
+ console_printf("invalid 'interval_max' parameter\n");
+ return rc;
+ }
+
+ params.slave_latency = parse_arg_uint16_dflt("latency", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'latency' parameter\n");
+ return rc;
+ }
+
+ params.timeout_multiplier = parse_arg_uint16_dflt("timeout", 0x0100, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'timeout' parameter\n");
+ return rc;
+ }
+
+ rc = btshell_l2cap_update(conn_handle, &params);
+ if (rc != 0) {
+ console_printf("error txing l2cap update; rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+ * $l2cap-create-server *
+ *****************************************************************************/
+
+int
+cmd_l2cap_create_server(int argc, char **argv)
+{
+ uint16_t psm = 0;
+ uint16_t mtu;
+ int error;
+ int accept_response = 0;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ psm = parse_arg_uint16("psm", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'psm' parameter\n");
+ return rc;
+ }
+
+ error = parse_arg_uint32_dflt("error", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'error' parameter\n");
+ return rc;
+ }
+
+ mtu = parse_arg_uint16_dflt("mtu", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'mtu' parameter\n");
+ return rc;
+ }
+
+ switch (error) {
+ case 1:
+ accept_response = BLE_HS_EAUTHEN;
+ break;
+ case 2:
+ accept_response = BLE_HS_EAUTHOR;
+ break;
+ case 3:
+ accept_response = BLE_HS_EENCRYPT_KEY_SZ;
+ break;
+ }
+
+ rc = btshell_l2cap_create_srv(psm, mtu, accept_response);
+ if (rc) {
+ console_printf("Server create error: 0x%02x\n", rc);
+ return rc;
+ }
+
+ console_printf("Server created successfully\n");
+ return 0;
+}
+
+/*****************************************************************************
+ * $l2cap-connect *
+ *****************************************************************************/
+
+int
+cmd_l2cap_connect(int argc, char **argv)
+{
+ uint16_t conn = 0;
+ uint16_t psm = 0;
+ uint16_t mtu;
+ uint8_t num;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ psm = parse_arg_uint16("psm", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'psm' parameter\n");
+ return rc;
+ }
+
+ mtu = parse_arg_uint16_dflt("mtu", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'mtu' parameter\n");
+ return rc;
+ }
+
+ num = parse_arg_uint8_dflt("num", 1, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'num' parameter\n");
+ return rc;
+ }
+
+ return btshell_l2cap_connect(conn, psm, mtu, num);
+}
+
+/*****************************************************************************
+ * $l2cap-disconnect *
+ *****************************************************************************/
+
+int
+cmd_l2cap_disconnect(int argc, char **argv)
+{
+ uint16_t conn;
+ uint16_t idx;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ idx = parse_arg_uint16("idx", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'idx' parameter\n");
+ return rc;
+ }
+
+ return btshell_l2cap_disconnect(conn, idx);
+}
+
+/*****************************************************************************
+ * $l2cap-send *
+ *****************************************************************************/
+
+int
+cmd_l2cap_send(int argc, char **argv)
+{
+ uint16_t conn;
+ uint16_t idx;
+ uint16_t bytes;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ idx = parse_arg_uint16("idx", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'idx' parameter\n");
+ return rc;
+ }
+
+ bytes = parse_arg_uint16("bytes", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'bytes' parameter\n");
+ return rc;
+ }
+
+ return btshell_l2cap_send(conn, idx, bytes);
+}
+
+int
+cmd_l2cap_show_coc(int argc, char **argv)
+{
+ struct btshell_conn *conn = NULL;
+ struct btshell_l2cap_coc *coc;
+ int i, j;
+
+ for (i = 0; i < btshell_num_conns; i++) {
+ conn = btshell_conns + i;
+
+ if (SLIST_EMPTY(&conn->coc_list)) {
+ continue;
+ }
+
+ console_printf("conn_handle: 0x%04x\n", conn->handle);
+ j = 0;
+ SLIST_FOREACH(coc, &conn->coc_list, next) {
+ console_printf(" idx: %i, chan pointer = %p\n", j++, coc->chan);
+ }
+ }
+
+ return 0;
+}
+
+int
+cmd_l2cap_reconfig(int argc, char **argv)
+{
+#if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC)
+ uint16_t conn;
+ uint16_t mtu;
+ uint8_t idxs[5];
+ int num;
+ int rc;
+
+ rc = parse_arg_all(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ conn = parse_arg_uint16("conn", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'conn' parameter\n");
+ return rc;
+ }
+
+ mtu = parse_arg_uint16_dflt("mtu", 0,&rc);
+ if (rc != 0) {
+ console_printf("invalid 'mtu' parameter\n");
+ return rc;
+ }
+
+ rc = parse_arg_uint8_list_with_separator("idxs", ",", 5, idxs, &num);
+ if (rc != 0) {
+ console_printf("invalid 'idxs' parameter\n");
+ return rc;
+ }
+
+ return btshell_l2cap_reconfig(conn, mtu, num, idxs);
+#else
+ console_printf("To enable this features set BLE_L2CAP_ENHANCED_COC\n");
+ return ENOTSUP;
+#endif
+}
diff --git a/src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.h b/src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.h
new file mode 100644
index 00000000..d366fe26
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.h
@@ -0,0 +1,33 @@
+/*
+ * 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 CMD_L2CAP_H
+#define CMD_L2CAP_H
+
+#include "cmd.h"
+
+int cmd_l2cap_update(int argc, char **argv);
+int cmd_l2cap_create_server(int argc, char **argv);
+int cmd_l2cap_connect(int argc, char **argv);
+int cmd_l2cap_disconnect(int argc, char **argv);
+int cmd_l2cap_send(int argc, char **argv);
+int cmd_l2cap_show_coc(int argc, char **argv);
+int cmd_l2cap_reconfig(int argc, char **argv);
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/btshell/src/gatt_svr.c b/src/libs/mynewt-nimble/apps/btshell/src/gatt_svr.c
new file mode 100644
index 00000000..99d44d05
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/btshell/src/gatt_svr.c
@@ -0,0 +1,638 @@
+/*
+ * 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 "bsp/bsp.h"
+#include "console/console.h"
+#include "host/ble_hs.h"
+#include "host/ble_uuid.h"
+#include "host/ble_gatt.h"
+#include "btshell.h"
+
+/* 0000xxxx-8c26-476f-89a7-a108033a69c7 */
+#define PTS_UUID_DECLARE(uuid16) \
+ ((const ble_uuid_t *) (&(ble_uuid128_t) BLE_UUID128_INIT( \
+ 0xc7, 0x69, 0x3a, 0x03, 0x08, 0xa1, 0xa7, 0x89, \
+ 0x6f, 0x47, 0x26, 0x8c, uuid16, uuid16 >> 8, 0x00, 0x00 \
+ )))
+
+#define PTS_SVC 0x0001
+#define PTS_CHR_READ 0x0002
+#define PTS_CHR_WRITE 0x0003
+#define PTS_CHR_RELIABLE_WRITE 0x0004
+#define PTS_CHR_WRITE_NO_RSP 0x0005
+#define PTS_CHR_READ_WRITE 0x0006
+#define PTS_CHR_READ_WRITE_ENC 0x0007
+#define PTS_CHR_READ_WRITE_AUTHEN 0x0008
+#define PTS_DSC_READ 0x0009
+#define PTS_DSC_WRITE 0x000a
+#define PTS_DSC_READ_WRITE 0x000b
+#define PTS_DSC_READ_WRITE_ENC 0x000c
+#define PTS_DSC_READ_WRITE_AUTHEN 0x000d
+
+#define PTS_LONG_SVC 0x0011
+#define PTS_LONG_CHR_READ 0x0012
+#define PTS_LONG_CHR_WRITE 0x0013
+#define PTS_LONG_CHR_RELIABLE_WRITE 0x0014
+#define PTS_LONG_CHR_READ_WRITE 0x0015
+#define PTS_LONG_CHR_READ_WRITE_ALT 0x0016
+#define PTS_LONG_CHR_READ_WRITE_ENC 0x0017
+#define PTS_LONG_CHR_READ_WRITE_AUTHEN 0x0018
+#define PTS_LONG_DSC_READ 0x0019
+#define PTS_LONG_DSC_WRITE 0x001a
+#define PTS_LONG_DSC_READ_WRITE 0x001b
+#define PTS_LONG_DSC_READ_WRITE_ENC 0x001c
+#define PTS_LONG_DSC_READ_WRITE_AUTHEN 0x001d
+
+#define PTS_INC_SVC 0x001e
+#define PTS_CHR_READ_WRITE_ALT 0x001f
+
+/**
+ * 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.
+ */
+
+/* 59462f12-9543-9999-12c8-58b459a2712d */
+static const ble_uuid128_t gatt_svr_svc_sec_test_uuid =
+ BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12,
+ 0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59);
+
+/* 5c3a659e-897e-45e1-b016-007107c96df6 */
+static const ble_uuid128_t gatt_svr_chr_sec_test_rand_uuid =
+ BLE_UUID128_INIT(0xf6, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
+ 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
+
+/* 5c3a659e-897e-45e1-b016-007107c96df7 */
+static const ble_uuid128_t gatt_svr_chr_sec_test_static_uuid =
+ BLE_UUID128_INIT(0xf7, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
+ 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
+
+/* 5c3a659e-897e-45e1-b016-007107c96df8 */
+static const ble_uuid128_t gatt_svr_chr_sec_test_static_auth_uuid =
+ BLE_UUID128_INIT(0xf8, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
+ 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
+
+static uint8_t gatt_svr_sec_test_static_val;
+
+static uint8_t gatt_svr_pts_static_val;
+static uint8_t gatt_svr_pts_static_long_val[30];
+static uint8_t gatt_svr_pts_static_long_val_alt[30];
+
+static int
+gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_access_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_long_access_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
+ {
+ /*** Service: PTS test. */
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = PTS_UUID_DECLARE(PTS_SVC),
+ .characteristics = (struct ble_gatt_chr_def[]) { {
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_READ),
+ .access_cb = gatt_svr_access_test,
+ .flags = BLE_GATT_CHR_F_READ,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_WRITE),
+ .access_cb = gatt_svr_access_test,
+ .flags = BLE_GATT_CHR_F_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_RELIABLE_WRITE),
+ .access_cb = gatt_svr_access_test,
+ .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_RELIABLE_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_WRITE_NO_RSP),
+ .access_cb = gatt_svr_access_test,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE_NO_RSP,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE),
+ .access_cb = gatt_svr_access_test,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ENC),
+ .access_cb = gatt_svr_access_test,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC |
+ BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC,
+ .min_key_size = 16,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_AUTHEN),
+ .access_cb = gatt_svr_access_test,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN |
+ BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN,
+
+ .descriptors = (struct ble_gatt_dsc_def[]){ {
+ .uuid = PTS_UUID_DECLARE(PTS_DSC_READ),
+ .access_cb = gatt_svr_access_test,
+ .att_flags = BLE_ATT_F_READ,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_DSC_WRITE),
+ .access_cb = gatt_svr_access_test,
+ .att_flags = BLE_ATT_F_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE),
+ .access_cb = gatt_svr_access_test,
+ .att_flags = BLE_ATT_F_READ | BLE_ATT_F_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE_ENC),
+ .access_cb = gatt_svr_access_test,
+ .att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_ENC |
+ BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_ENC,
+ .min_key_size = 16,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE_AUTHEN),
+ .access_cb = gatt_svr_access_test,
+ .att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_AUTHEN |
+ BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_AUTHEN,
+ }, {
+ 0, /* No more descriptors in this characteristic. */
+ } }
+ }, {
+ 0, /* No more characteristics in this service. */
+ } },
+ },
+
+ {
+ /*** Service: PTS long test. */
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_SVC),
+ .characteristics = (struct ble_gatt_chr_def[]) { {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ),
+ .access_cb = gatt_svr_long_access_test,
+ .flags = BLE_GATT_CHR_F_READ,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_WRITE),
+ .access_cb = gatt_svr_long_access_test,
+ .flags = BLE_GATT_CHR_F_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_RELIABLE_WRITE),
+ .access_cb = gatt_svr_long_access_test,
+ .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_RELIABLE_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE),
+ .access_cb = gatt_svr_long_access_test,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_ALT),
+ .access_cb = gatt_svr_long_access_test,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_ENC),
+ .access_cb = gatt_svr_long_access_test,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC |
+ BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC,
+ .min_key_size = 16,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_AUTHEN),
+ .access_cb = gatt_svr_long_access_test,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN |
+ BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN,
+
+ .descriptors = (struct ble_gatt_dsc_def[]){ {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ),
+ .access_cb = gatt_svr_long_access_test,
+ .att_flags = BLE_ATT_F_READ,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_WRITE),
+ .access_cb = gatt_svr_long_access_test,
+ .att_flags = BLE_ATT_F_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE),
+ .access_cb = gatt_svr_long_access_test,
+ .att_flags = BLE_ATT_F_READ | BLE_ATT_F_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE_ENC),
+ .access_cb = gatt_svr_long_access_test,
+ .att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_ENC |
+ BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_ENC,
+ .min_key_size = 16,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE_AUTHEN),
+ .access_cb = gatt_svr_long_access_test,
+ .att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_AUTHEN |
+ BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_AUTHEN,
+ }, {
+ 0, /* No more descriptors in this characteristic. */
+ } }
+ }, {
+ 0, /* No more characteristics in this service. */
+ } },
+ },
+
+ {
+ /*** Service: Security test. */
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = &gatt_svr_svc_sec_test_uuid.u,
+ .characteristics = (struct ble_gatt_chr_def[]) { {
+ /*** Characteristic: Random number generator. */
+ .uuid = &gatt_svr_chr_sec_test_rand_uuid.u,
+ .access_cb = gatt_svr_chr_access_sec_test,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC,
+ }, {
+ /*** Characteristic: Static value. */
+ .uuid = &gatt_svr_chr_sec_test_static_uuid.u,
+ .access_cb = gatt_svr_chr_access_sec_test,
+ .flags = BLE_GATT_CHR_F_READ |
+ BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC,
+ }, {
+ /*** Characteristic: Static value. */
+ .uuid = &gatt_svr_chr_sec_test_static_auth_uuid.u,
+ .access_cb = gatt_svr_chr_access_sec_test,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN,
+ }, {
+ 0, /* No more characteristics in this service. */
+ } },
+ },
+
+ {
+ 0, /* No more services. */
+ },
+};
+
+static const struct ble_gatt_svc_def *inc_svcs[] = {
+ &gatt_svr_svcs[0],
+ NULL,
+};
+
+static const struct ble_gatt_svc_def gatt_svr_inc_svcs[] = {
+ {
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = PTS_UUID_DECLARE(PTS_INC_SVC),
+ .includes = inc_svcs,
+ .characteristics = (struct ble_gatt_chr_def[]) {{
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ALT),
+ .access_cb = gatt_svr_access_test,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
+ }, {
+ 0, /* No more characteristics */
+ }, },
+ },
+
+ {
+ 0, /* No more services. */
+ },
+};
+
+static int
+gatt_svr_chr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len,
+ void *dst, uint16_t *len)
+{
+ uint16_t om_len;
+ int rc;
+
+ om_len = OS_MBUF_PKTLEN(om);
+ if (om_len < min_len || om_len > max_len) {
+ return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
+ }
+
+ rc = ble_hs_mbuf_to_flat(om, dst, max_len, len);
+ if (rc != 0) {
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+
+ return 0;
+}
+
+static int
+gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ const ble_uuid_t *uuid;
+ int rand_num;
+ int rc;
+
+ uuid = ctxt->chr->uuid;
+
+ /* Determine which characteristic is being accessed by examining its
+ * 128-bit UUID.
+ */
+
+ if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_rand_uuid.u) == 0) {
+ assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
+
+ /* Respond with a 32-bit random number. */
+ rand_num = rand();
+ rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_uuid.u) == 0 ||
+ ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_auth_uuid.u) == 0) {
+ switch (ctxt->op) {
+ case BLE_GATT_ACCESS_OP_READ_CHR:
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_sec_test_static_val,
+ sizeof gatt_svr_sec_test_static_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+
+ case BLE_GATT_ACCESS_OP_WRITE_CHR:
+ rc = gatt_svr_chr_write(ctxt->om,
+ sizeof gatt_svr_sec_test_static_val,
+ sizeof gatt_svr_sec_test_static_val,
+ &gatt_svr_sec_test_static_val, NULL);
+ return rc;
+
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+ }
+
+ /* Unknown characteristic; the nimble stack should not have called this
+ * function.
+ */
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+}
+
+/* This method is used for PTS testing only, to extract 16 bit value
+ * from 128 bit vendor specific UUID.
+ */
+static uint16_t
+extract_uuid16_from_pts_uuid128(const ble_uuid_t *uuid)
+{
+ const uint8_t *u8ptr;
+ uint16_t uuid16;
+
+ u8ptr = BLE_UUID128(uuid)->value;
+ uuid16 = u8ptr[12];
+ uuid16 |= (uint16_t)u8ptr[13] << 8;
+ return uuid16;
+}
+
+static int
+gatt_svr_access_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ uint16_t uuid16;
+ int rc;
+
+ uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid);
+ assert(uuid16 != 0);
+
+ switch (uuid16) {
+ case PTS_CHR_READ:
+ assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val,
+ sizeof gatt_svr_pts_static_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+
+ case PTS_CHR_WRITE:
+ case PTS_CHR_RELIABLE_WRITE:
+ case PTS_CHR_WRITE_NO_RSP:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+ rc = gatt_svr_chr_write(ctxt->om,0,
+ sizeof gatt_svr_pts_static_val,
+ &gatt_svr_pts_static_val, NULL);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val,
+ sizeof gatt_svr_pts_static_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+ assert(0);
+ break;
+ case PTS_CHR_READ_WRITE:
+ case PTS_CHR_READ_WRITE_ENC:
+ case PTS_CHR_READ_WRITE_AUTHEN:
+ case PTS_CHR_READ_WRITE_ALT:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+ rc = gatt_svr_chr_write(ctxt->om,0,
+ sizeof gatt_svr_pts_static_val,
+ &gatt_svr_pts_static_val, NULL);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val,
+ sizeof gatt_svr_pts_static_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+ assert(0);
+ break;
+ case PTS_DSC_READ:
+ assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC);
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val,
+ sizeof gatt_svr_pts_static_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+
+ case PTS_DSC_WRITE:
+ assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC);
+ rc = gatt_svr_chr_write(ctxt->om,0,
+ sizeof gatt_svr_pts_static_val,
+ &gatt_svr_pts_static_val, NULL);
+ return rc;
+
+ case PTS_DSC_READ_WRITE:
+ case PTS_DSC_READ_WRITE_ENC:
+ case PTS_DSC_READ_WRITE_AUTHEN:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) {
+ rc = gatt_svr_chr_write(ctxt->om,0,
+ sizeof gatt_svr_pts_static_val,
+ &gatt_svr_pts_static_val, NULL);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val,
+ sizeof gatt_svr_pts_static_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+ assert(0);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ return BLE_ATT_ERR_UNLIKELY;
+}
+
+static int
+gatt_svr_long_access_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ uint16_t uuid16;
+ int rc;
+
+ uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid);
+ assert(uuid16 != 0);
+
+ switch (uuid16) {
+ case PTS_LONG_CHR_READ:
+ assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val,
+ sizeof gatt_svr_pts_static_long_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+
+ case PTS_LONG_CHR_WRITE:
+ case PTS_LONG_CHR_RELIABLE_WRITE:
+ assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR);
+ rc = gatt_svr_chr_write(ctxt->om,0,
+ sizeof gatt_svr_pts_static_long_val,
+ &gatt_svr_pts_static_long_val, NULL);
+ return rc;
+
+ case PTS_LONG_CHR_READ_WRITE:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+ rc = gatt_svr_chr_write(ctxt->om,0,
+ sizeof gatt_svr_pts_static_long_val,
+ &gatt_svr_pts_static_long_val, NULL);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val,
+ sizeof gatt_svr_pts_static_long_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ case PTS_LONG_CHR_READ_WRITE_ALT:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+ rc = gatt_svr_chr_write(ctxt->om,0,
+ sizeof gatt_svr_pts_static_long_val_alt,
+ &gatt_svr_pts_static_long_val_alt, NULL);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val_alt,
+ sizeof gatt_svr_pts_static_long_val_alt);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ case PTS_LONG_CHR_READ_WRITE_ENC:
+ case PTS_LONG_CHR_READ_WRITE_AUTHEN:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+ rc = gatt_svr_chr_write(ctxt->om,0,
+ sizeof gatt_svr_pts_static_long_val,
+ &gatt_svr_pts_static_long_val, NULL);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val,
+ sizeof gatt_svr_pts_static_long_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ case PTS_LONG_DSC_READ:
+ assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC);
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val,
+ sizeof gatt_svr_pts_static_long_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+
+ case PTS_LONG_DSC_WRITE:
+ assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC);
+ rc = gatt_svr_chr_write(ctxt->om,0,
+ sizeof gatt_svr_pts_static_long_val,
+ &gatt_svr_pts_static_long_val, NULL);
+ return rc;
+
+ case PTS_LONG_DSC_READ_WRITE:
+ case PTS_LONG_DSC_READ_WRITE_ENC:
+ case PTS_LONG_DSC_READ_WRITE_AUTHEN:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) {
+ rc = gatt_svr_chr_write(ctxt->om,0,
+ sizeof gatt_svr_pts_static_long_val,
+ &gatt_svr_pts_static_long_val, NULL);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val,
+ sizeof gatt_svr_pts_static_long_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ default:
+ assert(0);
+ 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:
+ MODLOG_DFLT(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:
+ MODLOG_DFLT(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:
+ MODLOG_DFLT(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;
+ }
+}
+
+void
+gatt_svr_print_svcs(void)
+{
+ ble_gatts_show_local();
+}
+
+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;
+ }
+
+ rc = ble_gatts_count_cfg(gatt_svr_inc_svcs);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = ble_gatts_add_svcs(gatt_svr_inc_svcs);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/btshell/src/main.c b/src/libs/mynewt-nimble/apps/btshell/src/main.c
new file mode 100644
index 00000000..b7031836
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/btshell/src/main.c
@@ -0,0 +1,2630 @@
+/*
+ * 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 "os/mynewt.h"
+#include "bsp/bsp.h"
+#include "log/log.h"
+#include "stats/stats.h"
+#include "bsp/bsp.h"
+#include "hal/hal_gpio.h"
+#include "console/console.h"
+#include "btshell.h"
+#include "cmd.h"
+
+/* BLE */
+#include "nimble/ble.h"
+#include "nimble/nimble_opt.h"
+#include "nimble/ble_hci_trans.h"
+#include "host/ble_hs.h"
+#include "host/ble_hs_adv.h"
+#include "host/ble_uuid.h"
+#include "host/ble_att.h"
+#include "host/ble_gap.h"
+#include "host/ble_gatt.h"
+#include "host/ble_store.h"
+#include "host/ble_sm.h"
+
+/* Mandatory services. */
+#include "services/gap/ble_svc_gap.h"
+#include "services/gatt/ble_svc_gatt.h"
+
+/* XXX: An app should not include private headers from a library. The btshell
+ * app uses some of nimble's internal details for logging.
+ */
+#include "../src/ble_hs_conn_priv.h"
+#include "../src/ble_hs_atomic_priv.h"
+#include "../src/ble_hs_priv.h"
+
+#if MYNEWT_VAL(BLE_ROLE_CENTRAL)
+#define BTSHELL_MAX_SVCS 32
+#define BTSHELL_MAX_CHRS 64
+#define BTSHELL_MAX_DSCS 64
+#else
+#define BTSHELL_MAX_SVCS 1
+#define BTSHELL_MAX_CHRS 1
+#define BTSHELL_MAX_DSCS 1
+#endif
+
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
+#define BTSHELL_COC_MTU (256)
+/* We use same pool for incoming and outgoing sdu */
+#define BTSHELL_COC_BUF_COUNT (3 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM))
+
+#define INT_TO_PTR(x) (void *)((intptr_t)(x))
+#define PTR_TO_INT(x) (int) ((intptr_t)(x))
+#endif
+
+bssnz_t struct btshell_conn btshell_conns[MYNEWT_VAL(BLE_MAX_CONNECTIONS)];
+int btshell_num_conns;
+
+static os_membuf_t btshell_svc_mem[
+ OS_MEMPOOL_SIZE(BTSHELL_MAX_SVCS, sizeof(struct btshell_svc))
+];
+static struct os_mempool btshell_svc_pool;
+
+static os_membuf_t btshell_chr_mem[
+ OS_MEMPOOL_SIZE(BTSHELL_MAX_CHRS, sizeof(struct btshell_chr))
+];
+static struct os_mempool btshell_chr_pool;
+
+static os_membuf_t btshell_dsc_mem[
+ OS_MEMPOOL_SIZE(BTSHELL_MAX_DSCS, sizeof(struct btshell_dsc))
+];
+static struct os_mempool btshell_dsc_pool;
+
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
+static os_membuf_t btshell_coc_conn_mem[
+ OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM),
+ sizeof(struct btshell_l2cap_coc))
+];
+static struct os_mempool btshell_coc_conn_pool;
+
+static os_membuf_t btshell_sdu_coc_mem[
+ OS_MEMPOOL_SIZE(BTSHELL_COC_BUF_COUNT, BTSHELL_COC_MTU)
+];
+struct os_mbuf_pool sdu_os_mbuf_pool;
+static struct os_mempool sdu_coc_mbuf_mempool;
+#endif
+
+static struct os_callout btshell_tx_timer;
+struct btshell_tx_data_s
+{
+ uint16_t tx_num;
+ uint16_t tx_num_requested;
+ uint16_t tx_rate;
+ uint16_t tx_conn_handle;
+ uint16_t tx_len;
+ struct ble_hs_conn *conn;
+};
+static struct btshell_tx_data_s btshell_tx_data;
+int btshell_full_disc_prev_chr_val;
+
+struct ble_sm_sc_oob_data oob_data_local;
+struct ble_sm_sc_oob_data oob_data_remote;
+
+#define XSTR(s) STR(s)
+#ifndef STR
+#define STR(s) #s
+#endif
+
+
+#ifdef DEVICE_NAME
+#define BTSHELL_AUTO_DEVICE_NAME XSTR(DEVICE_NAME)
+#else
+#define BTSHELL_AUTO_DEVICE_NAME ""
+#endif
+
+#if MYNEWT_VAL(BLE_EXT_ADV)
+struct {
+ bool restart;
+ uint16_t conn_handle;
+} ext_adv_restart[BLE_ADV_INSTANCES];
+#endif
+
+static struct {
+ bool restart;
+ uint8_t own_addr_type;
+ ble_addr_t direct_addr;
+ int32_t duration_ms;
+ struct ble_gap_adv_params params;
+} adv_params;
+
+static void
+btshell_print_error(char *msg, uint16_t conn_handle,
+ const struct ble_gatt_error *error)
+{
+ if (msg == NULL) {
+ msg = "ERROR";
+ }
+
+ console_printf("%s: conn_handle=%d status=%d att_handle=%d\n",
+ msg, conn_handle, error->status, error->att_handle);
+}
+
+static void
+btshell_print_adv_fields(const struct ble_hs_adv_fields *fields)
+{
+ const uint8_t *u8p;
+ int i;
+
+ if (fields->flags != 0) {
+ console_printf(" flags=0x%02x:\n", fields->flags);
+
+ if (!(fields->flags & BLE_HS_ADV_F_DISC_LTD) &&
+ !(fields->flags & BLE_HS_ADV_F_DISC_GEN)) {
+ console_printf(" Non-discoverable mode\n");
+ }
+
+ if (fields->flags & BLE_HS_ADV_F_DISC_LTD) {
+ console_printf(" Limited discoverable mode\n");
+ }
+
+ if (fields->flags & BLE_HS_ADV_F_DISC_GEN) {
+ console_printf(" General discoverable mode\n");
+ }
+
+ if (fields->flags & BLE_HS_ADV_F_BREDR_UNSUP) {
+ console_printf(" BR/EDR not supported\n");
+ }
+ }
+
+ if (fields->uuids16 != NULL) {
+ console_printf(" uuids16(%scomplete)=",
+ fields->uuids16_is_complete ? "" : "in");
+ for (i = 0; i < fields->num_uuids16; i++) {
+ print_uuid(&fields->uuids16[i].u);
+ console_printf(" ");
+ }
+ console_printf("\n");
+ }
+
+ if (fields->uuids32 != NULL) {
+ console_printf(" uuids32(%scomplete)=",
+ fields->uuids32_is_complete ? "" : "in");
+ for (i = 0; i < fields->num_uuids32; i++) {
+ print_uuid(&fields->uuids32[i].u);
+ console_printf(" ");
+ }
+ console_printf("\n");
+ }
+
+ if (fields->uuids128 != NULL) {
+ console_printf(" uuids128(%scomplete)=",
+ fields->uuids128_is_complete ? "" : "in");
+ for (i = 0; i < fields->num_uuids128; i++) {
+ print_uuid(&fields->uuids128[i].u);
+ console_printf(" ");
+ }
+ console_printf("\n");
+ }
+
+ if (fields->name != NULL) {
+ console_printf(" name(%scomplete)=",
+ fields->name_is_complete ? "" : "in");
+ console_write((char *)fields->name, fields->name_len);
+ console_printf("\n");
+ }
+
+ if (fields->tx_pwr_lvl_is_present) {
+ console_printf(" tx_pwr_lvl=%d\n", fields->tx_pwr_lvl);
+ }
+
+ if (fields->slave_itvl_range != NULL) {
+ console_printf(" slave_itvl_range=");
+ print_bytes(fields->slave_itvl_range,
+ BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
+ console_printf("\n");
+ }
+
+ if (fields->svc_data_uuid16 != NULL) {
+ console_printf(" svc_data_uuid16=");
+ print_bytes(fields->svc_data_uuid16,
+ fields->svc_data_uuid16_len);
+ console_printf("\n");
+ }
+
+ if (fields->public_tgt_addr != NULL) {
+ console_printf(" public_tgt_addr=");
+ u8p = fields->public_tgt_addr;
+ for (i = 0; i < fields->num_public_tgt_addrs; i++) {
+ print_addr(u8p);
+ u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
+ }
+ console_printf("\n");
+ }
+
+ if (fields->appearance_is_present) {
+ console_printf(" appearance=0x%04x\n", fields->appearance);
+ }
+
+ if (fields->adv_itvl_is_present) {
+ console_printf(" adv_itvl=0x%04x\n", fields->adv_itvl);
+ }
+
+ if (fields->svc_data_uuid32 != NULL) {
+ console_printf(" svc_data_uuid32=");
+ print_bytes(fields->svc_data_uuid32,
+ fields->svc_data_uuid32_len);
+ console_printf("\n");
+ }
+
+ if (fields->svc_data_uuid128 != NULL) {
+ console_printf(" svc_data_uuid128=");
+ print_bytes(fields->svc_data_uuid128,
+ fields->svc_data_uuid128_len);
+ console_printf("\n");
+ }
+
+ if (fields->uri != NULL) {
+ console_printf(" uri=");
+ print_bytes(fields->uri, fields->uri_len);
+ console_printf("\n");
+ }
+
+ if (fields->mfg_data != NULL) {
+ console_printf(" mfg_data=");
+ print_bytes(fields->mfg_data, fields->mfg_data_len);
+ console_printf("\n");
+ }
+}
+
+static int
+btshell_conn_find_idx(uint16_t handle)
+{
+ int i;
+
+ for (i = 0; i < btshell_num_conns; i++) {
+ if (btshell_conns[i].handle == handle) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static struct btshell_conn *
+btshell_conn_find(uint16_t handle)
+{
+ int idx;
+
+ idx = btshell_conn_find_idx(handle);
+ if (idx == -1) {
+ return NULL;
+ } else {
+ return btshell_conns + idx;
+ }
+}
+
+static struct btshell_svc *
+btshell_svc_find_prev(struct btshell_conn *conn, uint16_t svc_start_handle)
+{
+ struct btshell_svc *prev;
+ struct btshell_svc *svc;
+
+ prev = NULL;
+ SLIST_FOREACH(svc, &conn->svcs, next) {
+ if (svc->svc.start_handle >= svc_start_handle) {
+ break;
+ }
+
+ prev = svc;
+ }
+
+ return prev;
+}
+
+static struct btshell_svc *
+btshell_svc_find(struct btshell_conn *conn, uint16_t svc_start_handle,
+ struct btshell_svc **out_prev)
+{
+ struct btshell_svc *prev;
+ struct btshell_svc *svc;
+
+ prev = btshell_svc_find_prev(conn, svc_start_handle);
+ if (prev == NULL) {
+ svc = SLIST_FIRST(&conn->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 btshell_svc *
+btshell_svc_find_range(struct btshell_conn *conn, uint16_t attr_handle)
+{
+ struct btshell_svc *svc;
+
+ SLIST_FOREACH(svc, &conn->svcs, next) {
+ if (svc->svc.start_handle <= attr_handle &&
+ svc->svc.end_handle >= attr_handle) {
+
+ return svc;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+btshell_chr_delete(struct btshell_chr *chr)
+{
+ struct btshell_dsc *dsc;
+
+ while ((dsc = SLIST_FIRST(&chr->dscs)) != NULL) {
+ SLIST_REMOVE_HEAD(&chr->dscs, next);
+ os_memblock_put(&btshell_dsc_pool, dsc);
+ }
+
+ os_memblock_put(&btshell_chr_pool, chr);
+}
+
+static void
+btshell_svc_delete(struct btshell_svc *svc)
+{
+ struct btshell_chr *chr;
+
+ while ((chr = SLIST_FIRST(&svc->chrs)) != NULL) {
+ SLIST_REMOVE_HEAD(&svc->chrs, next);
+ btshell_chr_delete(chr);
+ }
+
+ os_memblock_put(&btshell_svc_pool, svc);
+}
+
+static struct btshell_svc *
+btshell_svc_add(uint16_t conn_handle, const struct ble_gatt_svc *gatt_svc)
+{
+ struct btshell_conn *conn;
+ struct btshell_svc *prev;
+ struct btshell_svc *svc;
+
+ conn = btshell_conn_find(conn_handle);
+ if (conn == NULL) {
+ MODLOG_DFLT(DEBUG, "RECEIVED SERVICE FOR UNKNOWN CONNECTION; "
+ "HANDLE=%d\n",
+ conn_handle);
+ return NULL;
+ }
+
+ svc = btshell_svc_find(conn, gatt_svc->start_handle, &prev);
+ if (svc != NULL) {
+ /* Service already discovered. */
+ return svc;
+ }
+
+ svc = os_memblock_get(&btshell_svc_pool);
+ if (svc == NULL) {
+ MODLOG_DFLT(DEBUG, "OOM WHILE DISCOVERING SERVICE\n");
+ return NULL;
+ }
+ memset(svc, 0, sizeof *svc);
+
+ svc->svc = *gatt_svc;
+ SLIST_INIT(&svc->chrs);
+
+ if (prev == NULL) {
+ SLIST_INSERT_HEAD(&conn->svcs, svc, next);
+ } else {
+ SLIST_INSERT_AFTER(prev, svc, next);
+ }
+
+ return svc;
+}
+
+static struct btshell_chr *
+btshell_chr_find_prev(const struct btshell_svc *svc, uint16_t chr_val_handle)
+{
+ struct btshell_chr *prev;
+ struct btshell_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 btshell_chr *
+btshell_chr_find(const struct btshell_svc *svc, uint16_t chr_val_handle,
+ struct btshell_chr **out_prev)
+{
+ struct btshell_chr *prev;
+ struct btshell_chr *chr;
+
+ prev = btshell_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 struct btshell_chr *
+btshell_chr_add(uint16_t conn_handle, uint16_t svc_start_handle,
+ const struct ble_gatt_chr *gatt_chr)
+{
+ struct btshell_conn *conn;
+ struct btshell_chr *prev;
+ struct btshell_chr *chr;
+ struct btshell_svc *svc;
+
+ conn = btshell_conn_find(conn_handle);
+ if (conn == NULL) {
+ MODLOG_DFLT(DEBUG, "RECEIVED SERVICE FOR UNKNOWN CONNECTION; "
+ "HANDLE=%d\n",
+ conn_handle);
+ return NULL;
+ }
+
+ svc = btshell_svc_find(conn, svc_start_handle, NULL);
+ if (svc == NULL) {
+ MODLOG_DFLT(DEBUG, "CAN'T FIND SERVICE FOR DISCOVERED CHR; HANDLE=%d\n",
+ conn_handle);
+ return NULL;
+ }
+
+ chr = btshell_chr_find(svc, gatt_chr->val_handle, &prev);
+ if (chr != NULL) {
+ /* Characteristic already discovered. */
+ return chr;
+ }
+
+ chr = os_memblock_get(&btshell_chr_pool);
+ if (chr == NULL) {
+ MODLOG_DFLT(DEBUG, "OOM WHILE DISCOVERING CHARACTERISTIC\n");
+ return NULL;
+ }
+ 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 chr;
+}
+
+static struct btshell_dsc *
+btshell_dsc_find_prev(const struct btshell_chr *chr, uint16_t dsc_handle)
+{
+ struct btshell_dsc *prev;
+ struct btshell_dsc *dsc;
+
+ prev = NULL;
+ SLIST_FOREACH(dsc, &chr->dscs, next) {
+ if (dsc->dsc.handle >= dsc_handle) {
+ break;
+ }
+
+ prev = dsc;
+ }
+
+ return prev;
+}
+
+static struct btshell_dsc *
+btshell_dsc_find(const struct btshell_chr *chr, uint16_t dsc_handle,
+ struct btshell_dsc **out_prev)
+{
+ struct btshell_dsc *prev;
+ struct btshell_dsc *dsc;
+
+ prev = btshell_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 struct btshell_dsc *
+btshell_dsc_add(uint16_t conn_handle, uint16_t chr_val_handle,
+ const struct ble_gatt_dsc *gatt_dsc)
+{
+ struct btshell_conn *conn;
+ struct btshell_dsc *prev;
+ struct btshell_dsc *dsc;
+ struct btshell_svc *svc;
+ struct btshell_chr *chr;
+
+ conn = btshell_conn_find(conn_handle);
+ if (conn == NULL) {
+ MODLOG_DFLT(DEBUG, "RECEIVED SERVICE FOR UNKNOWN CONNECTION; "
+ "HANDLE=%d\n",
+ conn_handle);
+ return NULL;
+ }
+
+ svc = btshell_svc_find_range(conn, chr_val_handle);
+ if (svc == NULL) {
+ MODLOG_DFLT(DEBUG, "CAN'T FIND SERVICE FOR DISCOVERED DSC; HANDLE=%d\n",
+ conn_handle);
+ return NULL;
+ }
+
+ chr = btshell_chr_find(svc, chr_val_handle, NULL);
+ if (chr == NULL) {
+ MODLOG_DFLT(DEBUG, "CAN'T FIND CHARACTERISTIC FOR DISCOVERED DSC; "
+ "HANDLE=%d\n",
+ conn_handle);
+ return NULL;
+ }
+
+ dsc = btshell_dsc_find(chr, gatt_dsc->handle, &prev);
+ if (dsc != NULL) {
+ /* Descriptor already discovered. */
+ return dsc;
+ }
+
+ dsc = os_memblock_get(&btshell_dsc_pool);
+ if (dsc == NULL) {
+ console_printf("OOM WHILE DISCOVERING DESCRIPTOR\n");
+ return NULL;
+ }
+ 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 dsc;
+}
+
+static struct btshell_conn *
+btshell_conn_add(struct ble_gap_conn_desc *desc)
+{
+ struct btshell_conn *conn;
+
+ assert(btshell_num_conns < MYNEWT_VAL(BLE_MAX_CONNECTIONS));
+
+ conn = btshell_conns + btshell_num_conns;
+ btshell_num_conns++;
+
+ conn->handle = desc->conn_handle;
+ SLIST_INIT(&conn->svcs);
+ SLIST_INIT(&conn->coc_list);
+
+ return conn;
+}
+
+static void
+btshell_conn_delete_idx(int idx)
+{
+ struct btshell_conn *conn;
+ struct btshell_svc *svc;
+
+ assert(idx >= 0 && idx < btshell_num_conns);
+
+ conn = btshell_conns + idx;
+ while ((svc = SLIST_FIRST(&conn->svcs)) != NULL) {
+ SLIST_REMOVE_HEAD(&conn->svcs, next);
+ btshell_svc_delete(svc);
+ }
+
+ /* This '#if' is not strictly necessary. It is here to prevent a spurious
+ * warning from being reported.
+ */
+#if MYNEWT_VAL(BLE_MAX_CONNECTIONS) > 1
+ int i;
+ for (i = idx + 1; i < btshell_num_conns; i++) {
+ btshell_conns[i - 1] = btshell_conns[i];
+ }
+#endif
+
+ btshell_num_conns--;
+}
+
+static int
+btshell_on_mtu(uint16_t conn_handle, const struct ble_gatt_error *error,
+ uint16_t mtu, void *arg)
+{
+ switch (error->status) {
+ case 0:
+ console_printf("mtu exchange complete: conn_handle=%d mtu=%d\n",
+ conn_handle, mtu);
+ break;
+
+ default:
+ btshell_print_error(NULL, conn_handle, error);
+ break;
+ }
+
+ return 0;
+}
+
+static void
+btshell_full_disc_complete(int rc)
+{
+ console_printf("full discovery complete; rc=%d\n", rc);
+ btshell_full_disc_prev_chr_val = 0;
+}
+
+static void
+btshell_disc_full_dscs(uint16_t conn_handle)
+{
+ struct btshell_conn *conn;
+ struct btshell_chr *chr;
+ struct btshell_svc *svc;
+ int rc;
+
+ conn = btshell_conn_find(conn_handle);
+ if (conn == NULL) {
+ MODLOG_DFLT(DEBUG, "Failed to discover descriptors for conn=%d; "
+ "not connected\n", conn_handle);
+ btshell_full_disc_complete(BLE_HS_ENOTCONN);
+ return;
+ }
+
+ SLIST_FOREACH(svc, &conn->svcs, next) {
+ SLIST_FOREACH(chr, &svc->chrs, next) {
+ if (!chr_is_empty(svc, chr) &&
+ SLIST_EMPTY(&chr->dscs) &&
+ btshell_full_disc_prev_chr_val <= chr->chr.def_handle) {
+
+ rc = btshell_disc_all_dscs(conn_handle,
+ chr->chr.val_handle,
+ chr_end_handle(svc, chr));
+ if (rc != 0) {
+ btshell_full_disc_complete(rc);
+ }
+
+ btshell_full_disc_prev_chr_val = chr->chr.val_handle;
+ return;
+ }
+ }
+ }
+
+ /* All descriptors discovered. */
+ btshell_full_disc_complete(0);
+}
+
+static void
+btshell_disc_full_chrs(uint16_t conn_handle)
+{
+ struct btshell_conn *conn;
+ struct btshell_svc *svc;
+ int rc;
+
+ conn = btshell_conn_find(conn_handle);
+ if (conn == NULL) {
+ MODLOG_DFLT(DEBUG, "Failed to discover characteristics for conn=%d; "
+ "not connected\n", conn_handle);
+ btshell_full_disc_complete(BLE_HS_ENOTCONN);
+ return;
+ }
+
+ SLIST_FOREACH(svc, &conn->svcs, next) {
+ if (!svc->discovered) {
+ rc = btshell_disc_all_chrs_in_svc(conn_handle, svc);
+ if (rc != 0) {
+ btshell_full_disc_complete(rc);
+ }
+ return;
+ }
+ }
+
+ /* All characteristics discovered. */
+ btshell_disc_full_dscs(conn_handle);
+}
+
+static int
+btshell_on_disc_s(uint16_t conn_handle, const struct ble_gatt_error *error,
+ const struct ble_gatt_svc *service, void *arg)
+{
+ switch (error->status) {
+ case 0:
+ btshell_svc_add(conn_handle, service);
+ break;
+
+ case BLE_HS_EDONE:
+ console_printf("service discovery successful\n");
+ if (btshell_full_disc_prev_chr_val > 0) {
+ btshell_disc_full_chrs(conn_handle);
+ }
+ break;
+
+ default:
+ btshell_print_error(NULL, conn_handle, error);
+ break;
+ }
+
+ return 0;
+}
+
+static int
+btshell_on_disc_c(uint16_t conn_handle, const struct ble_gatt_error *error,
+ const struct ble_gatt_chr *chr, void *arg)
+{
+ intptr_t svc_start_handle;
+
+ svc_start_handle = (intptr_t)arg;
+
+ switch (error->status) {
+ case 0:
+ btshell_chr_add(conn_handle, svc_start_handle, chr);
+ break;
+
+ case BLE_HS_EDONE:
+ console_printf("characteristic discovery successful\n");
+ if (btshell_full_disc_prev_chr_val > 0) {
+ btshell_disc_full_chrs(conn_handle);
+ }
+ break;
+
+ default:
+ btshell_print_error(NULL, conn_handle, error);
+ break;
+ }
+
+ return 0;
+}
+
+static int
+btshell_on_disc_c_in_s(uint16_t conn_handle, const struct ble_gatt_error *error,
+ const struct ble_gatt_chr *chr, void *arg)
+{
+ struct btshell_svc *svc = arg;
+
+ switch (error->status) {
+ case 0:
+ btshell_chr_add(conn_handle, svc->svc.start_handle, chr);
+ break;
+
+ case BLE_HS_EDONE:
+ svc->discovered = true;
+ console_printf("characteristic discovery successful\n");
+ if (btshell_full_disc_prev_chr_val > 0) {
+ btshell_disc_full_chrs(conn_handle);
+ }
+ break;
+
+ default:
+ btshell_print_error(NULL, conn_handle, error);
+ break;
+ }
+
+ return 0;
+}
+
+static int
+btshell_on_disc_d(uint16_t conn_handle, const struct ble_gatt_error *error,
+ uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
+ void *arg)
+{
+ switch (error->status) {
+ case 0:
+ btshell_dsc_add(conn_handle, chr_val_handle, dsc);
+ break;
+
+ case BLE_HS_EDONE:
+ console_printf("descriptor discovery successful\n");
+ if (btshell_full_disc_prev_chr_val > 0) {
+ btshell_disc_full_dscs(conn_handle);
+ }
+ break;
+
+ default:
+ btshell_print_error(NULL, conn_handle, error);
+ break;
+ }
+
+ return 0;
+}
+
+static int
+btshell_on_read(uint16_t conn_handle, const struct ble_gatt_error *error,
+ struct ble_gatt_attr *attr, void *arg)
+{
+ switch (error->status) {
+ case 0:
+ console_printf("characteristic read; conn_handle=%d "
+ "attr_handle=%d len=%d value=", conn_handle,
+ attr->handle, OS_MBUF_PKTLEN(attr->om));
+ print_mbuf(attr->om);
+ console_printf("\n");
+ break;
+
+ case BLE_HS_EDONE:
+ console_printf("characteristic read complete\n");
+ break;
+
+ default:
+ btshell_print_error(NULL, conn_handle, error);
+ break;
+ }
+
+ return 0;
+}
+
+static int
+btshell_on_write(uint16_t conn_handle, const struct ble_gatt_error *error,
+ struct ble_gatt_attr *attr, void *arg)
+{
+ switch (error->status) {
+ case 0:
+ console_printf("characteristic write complete; conn_handle=%d "
+ "attr_handle=%d\n", conn_handle, attr->handle);
+ break;
+
+ default:
+ btshell_print_error(NULL, conn_handle, error);
+ break;
+ }
+
+ return 0;
+}
+
+static int
+btshell_on_write_reliable(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ struct ble_gatt_attr *attrs, uint8_t num_attrs,
+ void *arg)
+{
+ int i;
+
+ switch (error->status) {
+ case 0:
+ console_printf("characteristic write reliable complete; "
+ "conn_handle=%d", conn_handle);
+
+ for (i = 0; i < num_attrs; i++) {
+ console_printf(" attr_handle=%d len=%d value=", attrs[i].handle,
+ OS_MBUF_PKTLEN(attrs[i].om));
+ print_mbuf(attrs[i].om);
+ }
+ console_printf("\n");
+ break;
+
+ default:
+ btshell_print_error(NULL, conn_handle, error);
+ break;
+ }
+
+ return 0;
+}
+
+static void
+btshell_decode_adv_data(const uint8_t *adv_data, uint8_t adv_data_len, void *arg)
+{
+ struct btshell_scan_opts *scan_opts = arg;
+ struct ble_hs_adv_fields fields;
+
+ console_printf(" data_length=%d data=", adv_data_len);
+
+ if (scan_opts) {
+ adv_data_len = min(adv_data_len, scan_opts->limit);
+ }
+
+ print_bytes(adv_data, adv_data_len);
+
+ console_printf(" fields:\n");
+ ble_hs_adv_parse_fields(&fields, adv_data, adv_data_len);
+ btshell_print_adv_fields(&fields);
+}
+
+#if MYNEWT_VAL(BLE_EXT_ADV)
+static void
+btshell_decode_event_type(struct ble_gap_ext_disc_desc *desc, void *arg)
+{
+ struct btshell_scan_opts *scan_opts = arg;
+ uint8_t directed = 0;
+
+ if (desc->props & BLE_HCI_ADV_LEGACY_MASK) {
+ if (scan_opts && scan_opts->ignore_legacy) {
+ return;
+ }
+
+ console_printf("Legacy PDU type %d", desc->legacy_event_type);
+ if (desc->legacy_event_type == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
+ directed = 1;
+ }
+ goto common_data;
+ } else {
+ if (scan_opts && scan_opts->periodic_only && desc->periodic_adv_itvl == 0) {
+ return;
+ }
+ }
+
+ console_printf("Extended adv: ");
+ if (desc->props & BLE_HCI_ADV_CONN_MASK) {
+ console_printf("'conn' ");
+ }
+ if (desc->props & BLE_HCI_ADV_SCAN_MASK) {
+ console_printf("'scan' ");
+ }
+ if (desc->props & BLE_HCI_ADV_DIRECT_MASK) {
+ console_printf("'dir' ");
+ directed = 1;
+ }
+
+ if (desc->props & BLE_HCI_ADV_SCAN_RSP_MASK) {
+ console_printf("'scan rsp' ");
+ }
+
+ switch(desc->data_status) {
+ case BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE:
+ console_printf("complete");
+ break;
+ case BLE_GAP_EXT_ADV_DATA_STATUS_INCOMPLETE:
+ console_printf("incomplete");
+ break;
+ case BLE_GAP_EXT_ADV_DATA_STATUS_TRUNCATED:
+ console_printf("truncated");
+ break;
+ default:
+ console_printf("reserved %d", desc->data_status);
+ break;
+ }
+
+common_data:
+ console_printf(" rssi=%d txpower=%d, pphy=%d, sphy=%d, sid=%d,"
+ " periodic_adv_itvl=%u, addr_type=%d addr=",
+ desc->rssi, desc->tx_power, desc->prim_phy, desc->sec_phy,
+ desc->sid, desc->periodic_adv_itvl, desc->addr.type);
+ print_addr(desc->addr.val);
+ if (directed) {
+ console_printf(" init_addr_type=%d inita=", desc->direct_addr.type);
+ print_addr(desc->direct_addr.val);
+ }
+
+ console_printf("\n");
+
+ if(!desc->length_data) {
+ return;
+ }
+
+ btshell_decode_adv_data(desc->data, desc->length_data, arg);
+}
+#endif
+
+static int
+btshell_restart_adv(struct ble_gap_event *event)
+{
+ int rc = 0;
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ uint8_t i;
+#endif
+
+ if (event->type != BLE_GAP_EVENT_DISCONNECT) {
+ return -1;
+ }
+
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ for (i = 0; i < BLE_ADV_INSTANCES; ++i) {
+ if (ext_adv_restart[i].restart &&
+ (ext_adv_restart[i].conn_handle ==
+ event->disconnect.conn.conn_handle)) {
+ rc = ble_gap_ext_adv_start(i, 0, 0);
+ break;
+ }
+ }
+#else
+ if (!adv_params.restart) {
+ return 0;
+ }
+
+ rc = ble_gap_adv_start(adv_params.own_addr_type, &adv_params.direct_addr,
+ adv_params.duration_ms, &adv_params.params,
+ btshell_gap_event, NULL);
+#endif
+
+ return rc;
+}
+
+#if MYNEWT_VAL(BLE_PERIODIC_ADV)
+struct psync {
+ bool established;
+ unsigned int complete;
+ unsigned int truncated;
+ size_t off;
+ bool changed;
+ uint8_t data[1650]; /* TODO make this configurable */
+};
+
+static struct psync g_periodic_data[MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)];
+
+void
+btshell_sync_stats(uint16_t handle)
+{
+ struct psync *psync;
+
+ if (handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) {
+ return;
+ }
+
+ psync = &g_periodic_data[handle];
+ if (!psync->established) {
+ console_printf("Sync not established\n");
+ return;
+ }
+
+ console_printf("completed=%u truncated=%u\n",
+ psync->complete, psync->truncated);
+}
+
+static void
+handle_periodic_report(struct ble_gap_event *event)
+{
+ struct psync *psync;
+ uint16_t handle = event->periodic_report.sync_handle;
+
+ if (handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) {
+ return;
+ }
+
+ psync = &g_periodic_data[handle];
+
+ if (psync->changed ||
+ memcmp(psync->data + psync->off, event->periodic_report.data,
+ event->periodic_report.data_length)) {
+ /* first fragment with changed data */
+ if (!psync->changed) {
+ console_printf("Sync data changed, completed=%u, truncated=%u\n",
+ psync->complete, psync->truncated);
+ }
+
+ psync->changed = true;
+
+ console_printf("Sync report handle=%u status=", handle);
+ switch(event->periodic_report.data_status) {
+ case BLE_HCI_PERIODIC_DATA_STATUS_COMPLETE:
+ console_printf("complete");
+ break;
+ case BLE_HCI_PERIODIC_DATA_STATUS_INCOMPLETE:
+ console_printf("incomplete");
+ break;
+ case BLE_HCI_PERIODIC_DATA_STATUS_TRUNCATED:
+ console_printf("truncated");
+ break;
+ default:
+ console_printf("reserved 0x%x", event->periodic_report.data_status);
+ break;
+ }
+
+ btshell_decode_adv_data(event->periodic_report.data,
+ event->periodic_report.data_length, NULL);
+
+ psync->complete = 0;
+ psync->truncated = 0;
+ }
+
+ /* cache data */
+ memcpy(psync->data + psync->off, event->periodic_report.data,
+ event->periodic_report.data_length);
+
+ switch(event->periodic_report.data_status) {
+ case BLE_HCI_PERIODIC_DATA_STATUS_INCOMPLETE:
+ psync->off += event->periodic_report.data_length;
+ break;
+ case BLE_HCI_PERIODIC_DATA_STATUS_COMPLETE:
+ psync->complete++;
+ psync->off = 0;
+ psync->changed = false;
+ break;
+ case BLE_HCI_PERIODIC_DATA_STATUS_TRUNCATED:
+ psync->truncated++;
+ psync->off = 0;
+ psync->changed = false;
+ break;
+ default:
+ break;
+ }
+}
+#endif
+
+int
+btshell_gap_event(struct ble_gap_event *event, void *arg)
+{
+ struct ble_gap_conn_desc desc;
+ int conn_idx;
+ int rc;
+#if MYNEWT_VAL(BLE_PERIODIC_ADV)
+ struct psync *psync;
+#endif
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ console_printf("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);
+ print_conn_desc(&desc);
+ btshell_conn_add(&desc);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ console_printf("disconnect; reason=%d ", event->disconnect.reason);
+ print_conn_desc(&event->disconnect.conn);
+
+ conn_idx = btshell_conn_find_idx(event->disconnect.conn.conn_handle);
+ if (conn_idx != -1) {
+ btshell_conn_delete_idx(conn_idx);
+ }
+
+ return btshell_restart_adv(event);
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ case BLE_GAP_EVENT_EXT_DISC:
+ btshell_decode_event_type(&event->ext_disc, arg);
+ return 0;
+#endif
+ case BLE_GAP_EVENT_DISC:
+ console_printf("received advertisement; event_type=%d rssi=%d "
+ "addr_type=%d addr=", event->disc.event_type,
+ event->disc.rssi, event->disc.addr.type);
+ print_addr(event->disc.addr.val);
+
+ /*
+ * There is no adv data to print in case of connectable
+ * directed advertising
+ */
+ if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
+ console_printf("\nConnectable directed advertising event\n");
+ return 0;
+ }
+
+ btshell_decode_adv_data(event->disc.data, event->disc.length_data, arg);
+
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE:
+ console_printf("connection updated; status=%d ",
+ event->conn_update.status);
+ rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
+ assert(rc == 0);
+ print_conn_desc(&desc);
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE_REQ:
+ console_printf("connection update request\n");
+ *event->conn_update_req.self_params =
+ *event->conn_update_req.peer_params;
+ return 0;
+
+ case BLE_GAP_EVENT_PASSKEY_ACTION:
+ console_printf("passkey action event; action=%d",
+ event->passkey.params.action);
+ if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
+ console_printf(" numcmp=%lu",
+ (unsigned long)event->passkey.params.numcmp);
+ }
+ console_printf("\n");
+ return 0;
+
+
+ case BLE_GAP_EVENT_DISC_COMPLETE:
+ console_printf("discovery complete; reason=%d\n",
+ event->disc_complete.reason);
+ return 0;
+
+ case BLE_GAP_EVENT_ADV_COMPLETE:
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ console_printf("advertise complete; reason=%d, instance=%u, handle=%d\n",
+ event->adv_complete.reason, event->adv_complete.instance,
+ event->adv_complete.conn_handle);
+
+ ext_adv_restart[event->adv_complete.instance].conn_handle =
+ event->adv_complete.conn_handle;
+#else
+ console_printf("advertise complete; reason=%d\n",
+ event->adv_complete.reason);
+#endif
+ return 0;
+
+ case BLE_GAP_EVENT_ENC_CHANGE:
+ console_printf("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:
+ console_printf("notification rx event; attr_handle=%d indication=%d "
+ "len=%d data=",
+ event->notify_rx.attr_handle,
+ event->notify_rx.indication,
+ OS_MBUF_PKTLEN(event->notify_rx.om));
+
+ print_mbuf(event->notify_rx.om);
+ console_printf("\n");
+ return 0;
+
+ case BLE_GAP_EVENT_NOTIFY_TX:
+ console_printf("notification tx event; status=%d attr_handle=%d "
+ "indication=%d\n",
+ event->notify_tx.status,
+ event->notify_tx.attr_handle,
+ event->notify_tx.indication);
+ return 0;
+
+ case BLE_GAP_EVENT_SUBSCRIBE:
+ console_printf("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);
+ return 0;
+
+ case BLE_GAP_EVENT_MTU:
+ console_printf("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_IDENTITY_RESOLVED:
+ console_printf("identity resolved ");
+ rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &desc);
+ assert(rc == 0);
+ print_conn_desc(&desc);
+ return 0;
+
+ case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
+ console_printf("PHY update complete; status=%d, conn_handle=%d "
+ " tx_phy=%d, rx_phy=%d\n",
+ event->phy_updated.status,
+ event->phy_updated.conn_handle,
+ event->phy_updated.tx_phy,
+ event->phy_updated.rx_phy);
+ 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;
+#if MYNEWT_VAL(BLE_PERIODIC_ADV)
+ case BLE_GAP_EVENT_PERIODIC_SYNC:
+ if (event->periodic_sync.status) {
+ console_printf("Periodic Sync Establishment Failed; status=%u\n",
+ event->periodic_sync.status);
+ } else {
+ console_printf("Periodic Sync Established; sync_handle=%u sid=%u "
+ "phy=%u adv_interval=%u ca=%u addr_type=%u addr=",
+ event->periodic_sync.sync_handle,
+ event->periodic_sync.sid, event->periodic_sync.adv_phy,
+ event->periodic_sync.per_adv_ival,
+ event->periodic_sync.adv_clk_accuracy,
+ event->periodic_sync.adv_addr.type);
+ print_addr(event->periodic_sync.adv_addr.val);
+ console_printf("\n");
+
+ /* TODO non-NimBLE controllers may not start handles from 0 */
+ if (event->periodic_sync.sync_handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) {
+ console_printf("Unable to prepare cache for sync data\n");
+ } else {
+ psync = &g_periodic_data[event->periodic_sync.sync_handle];
+ memset(psync, 0, sizeof(*psync));
+ psync->changed = true;
+ psync->established = true;
+ }
+ }
+ return 0;
+ case BLE_GAP_EVENT_PERIODIC_SYNC_LOST:
+ /* TODO non-NimBLE controllers may not start handles from 0 */
+ if (event->periodic_sync_lost.sync_handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) {
+ console_printf("Periodic Sync Lost; sync_handle=%d reason=%d\n",
+ event->periodic_sync_lost.sync_handle,
+ event->periodic_sync_lost.reason);
+ } else {
+ psync = &g_periodic_data[event->periodic_sync_lost.sync_handle];
+
+ console_printf("Periodic Sync Lost; sync_handle=%d reason=%d completed=%u truncated=%u\n",
+ event->periodic_sync_lost.sync_handle,
+ event->periodic_sync_lost.reason,
+ psync->complete, psync->truncated);
+
+ memset(psync, 0, sizeof(*psync));
+ }
+ return 0;
+ case BLE_GAP_EVENT_PERIODIC_REPORT:
+ handle_periodic_report(event);
+ return 0;
+#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER)
+ case BLE_GAP_EVENT_PERIODIC_TRANSFER:
+ console_printf("Periodic Sync Transfer Received on conn=%u; status=%u,"
+ " sync_handle=%u sid=%u phy=%u adv_interval=%u ca=%u "
+ "addr_type=%u addr=",
+ event->periodic_transfer.conn_handle,
+ event->periodic_transfer.status,
+ event->periodic_transfer.sync_handle,
+ event->periodic_transfer.sid,
+ event->periodic_transfer.adv_phy,
+ event->periodic_transfer.per_adv_itvl,
+ event->periodic_transfer.adv_clk_accuracy,
+ event->periodic_transfer.adv_addr.type);
+ print_addr(event->periodic_transfer.adv_addr.val);
+ console_printf("\n");
+
+ if (!event->periodic_transfer.status) {
+ /* TODO non-NimBLE controllers may not start handles from 0 */
+ if (event->periodic_transfer.sync_handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) {
+ console_printf("Unable to prepare cache for sync data\n");
+ } else {
+ psync = &g_periodic_data[event->periodic_transfer.sync_handle];
+ memset(psync, 0, sizeof(*psync));
+ psync->changed = true;
+ psync->established = true;
+ }
+ }
+ return 0;
+#endif
+#endif
+ default:
+ return 0;
+ }
+}
+
+static void
+btshell_on_l2cap_update(uint16_t conn_handle, int status, void *arg)
+{
+ console_printf("l2cap update complete; conn_handle=%d status=%d\n",
+ conn_handle, status);
+}
+
+static void
+btshell_tx_timer_cb(struct os_event *ev)
+{
+ uint8_t i;
+ uint8_t len;
+ int32_t timeout;
+ struct ble_l2cap_hdr l2cap_hdr;
+ struct os_mbuf *om;
+
+ if ((btshell_tx_data.tx_num == 0) || (btshell_tx_data.tx_len == 0)) {
+ return;
+ }
+
+ console_printf("Sending %d/%d len: %d\n",
+ btshell_tx_data.tx_num_requested - btshell_tx_data.tx_num + 1,
+ btshell_tx_data.tx_num_requested, btshell_tx_data.tx_len);
+
+ len = btshell_tx_data.tx_len;
+
+ om = NULL;
+ if (os_msys_num_free() >= 4) {
+ om = os_msys_get_pkthdr(len + BLE_L2CAP_HDR_SZ, BLE_HCI_DATA_HDR_SZ);
+ }
+
+ if (om) {
+ /*
+ * NOTE: CID is 0xffff so it is not confused with valid l2cap channel.
+ * The rest of the data gets filled with incrementing pattern starting
+ * from 0.
+ */
+ put_le16(&l2cap_hdr.len, len);
+ put_le16(&l2cap_hdr.cid, 0xffff);
+
+ os_mbuf_append(om, (void *)&l2cap_hdr, BLE_L2CAP_HDR_SZ);
+
+ for (i = 0; i < len; ++i) {
+ os_mbuf_append(om, (void *)&i, 1);
+ }
+
+ ble_hs_lock();
+ ble_hs_hci_acl_tx_now(btshell_tx_data.conn, &om);
+ ble_hs_unlock();
+
+ --btshell_tx_data.tx_num;
+ }
+
+ if (btshell_tx_data.tx_num) {
+ timeout = (int32_t)btshell_tx_data.tx_rate;
+ timeout = (timeout * OS_TICKS_PER_SEC) / 1000;
+ os_callout_reset(&btshell_tx_timer, timeout);
+ }
+}
+
+int
+btshell_exchange_mtu(uint16_t conn_handle)
+{
+ int rc;
+
+ rc = ble_gattc_exchange_mtu(conn_handle, btshell_on_mtu, NULL);
+ return rc;
+}
+
+int
+btshell_disc_all_chrs(uint16_t conn_handle, uint16_t start_handle,
+ uint16_t end_handle)
+{
+ intptr_t svc_start_handle;
+ int rc;
+
+ svc_start_handle = start_handle;
+ rc = ble_gattc_disc_all_chrs(conn_handle, start_handle, end_handle,
+ btshell_on_disc_c, (void *)svc_start_handle);
+ return rc;
+}
+
+int
+btshell_disc_all_chrs_in_svc(uint16_t conn_handle, struct btshell_svc *svc)
+{
+ int rc;
+
+ rc = ble_gattc_disc_all_chrs(conn_handle, svc->svc.start_handle,
+ svc->svc.end_handle, btshell_on_disc_c_in_s,
+ svc);
+ return rc;
+}
+
+int
+btshell_disc_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle,
+ uint16_t end_handle, const ble_uuid_t *uuid)
+{
+ intptr_t svc_start_handle;
+ int rc;
+
+ svc_start_handle = start_handle;
+ rc = ble_gattc_disc_chrs_by_uuid(conn_handle, start_handle, end_handle,
+ uuid, btshell_on_disc_c,
+ (void *)svc_start_handle);
+ return rc;
+}
+
+int
+btshell_disc_svcs(uint16_t conn_handle)
+{
+ int rc;
+
+ rc = ble_gattc_disc_all_svcs(conn_handle, btshell_on_disc_s, NULL);
+ return rc;
+}
+
+int
+btshell_disc_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid)
+{
+ int rc;
+
+ rc = ble_gattc_disc_svc_by_uuid(conn_handle, uuid,
+ btshell_on_disc_s, NULL);
+ return rc;
+}
+
+int
+btshell_disc_all_dscs(uint16_t conn_handle, uint16_t start_handle,
+ uint16_t end_handle)
+{
+ int rc;
+
+ rc = ble_gattc_disc_all_dscs(conn_handle, start_handle, end_handle,
+ btshell_on_disc_d, NULL);
+ return rc;
+}
+
+int
+btshell_disc_full(uint16_t conn_handle)
+{
+ struct btshell_conn *conn;
+ struct btshell_svc *svc;
+
+ /* Undiscover everything first. */
+ conn = btshell_conn_find(conn_handle);
+ if (conn == NULL) {
+ return BLE_HS_ENOTCONN;
+ }
+
+ while ((svc = SLIST_FIRST(&conn->svcs)) != NULL) {
+ SLIST_REMOVE_HEAD(&conn->svcs, next);
+ btshell_svc_delete(svc);
+ }
+
+ btshell_full_disc_prev_chr_val = 1;
+ btshell_disc_svcs(conn_handle);
+
+ return 0;
+}
+
+int
+btshell_find_inc_svcs(uint16_t conn_handle, uint16_t start_handle,
+ uint16_t end_handle)
+{
+ int rc;
+
+ rc = ble_gattc_find_inc_svcs(conn_handle, start_handle, end_handle,
+ btshell_on_disc_s, NULL);
+ return rc;
+}
+
+int
+btshell_read(uint16_t conn_handle, uint16_t attr_handle)
+{
+ struct os_mbuf *om;
+ int rc;
+
+ if (conn_handle == BLE_HS_CONN_HANDLE_NONE) {
+ rc = ble_att_svr_read_local(attr_handle, &om);
+ if (rc == 0) {
+ console_printf("read local; attr_handle=%d len=%d value=",
+ attr_handle, OS_MBUF_PKTLEN(om));
+ print_mbuf(om);
+ console_printf("\n");
+
+ os_mbuf_free_chain(om);
+ }
+ } else {
+ rc = ble_gattc_read(conn_handle, attr_handle, btshell_on_read, NULL);
+ }
+ return rc;
+}
+
+int
+btshell_read_long(uint16_t conn_handle, uint16_t attr_handle, uint16_t offset)
+{
+ int rc;
+
+ rc = ble_gattc_read_long(conn_handle, attr_handle, offset,
+ btshell_on_read, NULL);
+ return rc;
+}
+
+int
+btshell_read_by_uuid(uint16_t conn_handle, uint16_t start_handle,
+ uint16_t end_handle, const ble_uuid_t *uuid)
+{
+ int rc;
+
+ rc = ble_gattc_read_by_uuid(conn_handle, start_handle, end_handle, uuid,
+ btshell_on_read, NULL);
+ return rc;
+}
+
+int
+btshell_read_mult(uint16_t conn_handle, uint16_t *attr_handles,
+ int num_attr_handles)
+{
+ int rc;
+
+ rc = ble_gattc_read_mult(conn_handle, attr_handles, num_attr_handles,
+ btshell_on_read, NULL);
+ return rc;
+}
+
+int
+btshell_write(uint16_t conn_handle, uint16_t attr_handle, struct os_mbuf *om)
+{
+ int rc;
+
+ if (conn_handle == BLE_HS_CONN_HANDLE_NONE) {
+ rc = ble_att_svr_write_local(attr_handle, om);
+ } else {
+ rc = ble_gattc_write(conn_handle, attr_handle, om,
+ btshell_on_write, NULL);
+ }
+
+ return rc;
+}
+
+int
+btshell_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle,
+ struct os_mbuf *om)
+{
+ int rc;
+
+ rc = ble_gattc_write_no_rsp(conn_handle, attr_handle, om);
+
+ return rc;
+}
+
+int
+btshell_write_long(uint16_t conn_handle, uint16_t attr_handle,
+ uint16_t offset, struct os_mbuf *om)
+{
+ int rc;
+
+ rc = ble_gattc_write_long(conn_handle, attr_handle, offset,
+ om, btshell_on_write, NULL);
+ return rc;
+}
+
+int
+btshell_write_reliable(uint16_t conn_handle,
+ struct ble_gatt_attr *attrs,
+ int num_attrs)
+{
+ int rc;
+
+ rc = ble_gattc_write_reliable(conn_handle, attrs, num_attrs,
+ btshell_on_write_reliable, NULL);
+ return rc;
+}
+
+#if MYNEWT_VAL(BLE_EXT_ADV)
+int
+btshell_ext_adv_configure(uint8_t instance,
+ const struct ble_gap_ext_adv_params *params,
+ int8_t *selected_tx_power)
+{
+ return ble_gap_ext_adv_configure(instance, params, selected_tx_power,
+ btshell_gap_event, NULL);
+}
+
+int
+btshell_ext_adv_start(uint8_t instance, int duration,
+ int max_events, bool restart)
+{
+ int rc;
+
+ /* Advertising restart doesn't make sense
+ * with limited duration or events
+ */
+ if (restart && (duration == 0) && (max_events == 0)) {
+ ext_adv_restart[instance].restart = restart;
+ }
+
+ rc = ble_gap_ext_adv_start(instance, duration, max_events);
+
+ return rc;
+}
+
+int
+btshell_ext_adv_stop(uint8_t instance)
+{
+ int rc;
+
+ ext_adv_restart[instance].restart = false;
+
+ rc = ble_gap_ext_adv_stop(instance);
+
+ return rc;
+}
+#endif
+
+int
+btshell_adv_stop(void)
+{
+ int rc;
+
+ adv_params.restart = false;
+
+ rc = ble_gap_adv_stop();
+ return rc;
+}
+
+int
+btshell_adv_start(uint8_t own_addr_type, const ble_addr_t *direct_addr,
+ int32_t duration_ms, const struct ble_gap_adv_params *params,
+ bool restart)
+{
+ int rc;
+
+ if (restart) {
+ adv_params.restart = restart;
+ adv_params.own_addr_type = own_addr_type;
+ adv_params.duration_ms = duration_ms;
+
+ if (direct_addr) {
+ memcpy(&adv_params.direct_addr, direct_addr, sizeof(adv_params.direct_addr));
+ }
+
+ if (params) {
+ memcpy(&adv_params.params, params, sizeof(adv_params.params));
+ }
+ }
+
+ rc = ble_gap_adv_start(own_addr_type, direct_addr, duration_ms, params,
+ btshell_gap_event, NULL);
+ return rc;
+}
+
+int
+btshell_conn_initiate(uint8_t own_addr_type, const ble_addr_t *peer_addr,
+ int32_t duration_ms, struct ble_gap_conn_params *params)
+{
+ int rc;
+
+ rc = ble_gap_connect(own_addr_type, peer_addr, duration_ms, params,
+ btshell_gap_event, NULL);
+
+ return rc;
+}
+
+int
+btshell_ext_conn_initiate(uint8_t own_addr_type, const ble_addr_t *peer_addr,
+ int32_t duration_ms,
+ struct ble_gap_conn_params *phy_1m_params,
+ struct ble_gap_conn_params *phy_2m_params,
+ struct ble_gap_conn_params *phy_coded_params)
+{
+#if !MYNEWT_VAL(BLE_EXT_ADV)
+ console_printf("BLE extended advertising not supported.");
+ console_printf(" Configure nimble host to enable it\n");
+ return 0;
+#else
+ int rc;
+ uint8_t phy_mask = 0;
+
+ if (phy_1m_params) {
+ phy_mask |= BLE_GAP_LE_PHY_1M_MASK;
+ }
+
+ if (phy_2m_params) {
+ phy_mask |= BLE_GAP_LE_PHY_2M_MASK;
+ }
+
+ if (phy_coded_params) {
+ phy_mask |= BLE_GAP_LE_PHY_CODED_MASK;
+ }
+
+ rc = ble_gap_ext_connect(own_addr_type, peer_addr, duration_ms, phy_mask,
+ phy_1m_params, phy_2m_params, phy_coded_params,
+ btshell_gap_event, NULL);
+
+ return rc;
+#endif
+}
+
+int
+btshell_conn_cancel(void)
+{
+ int rc;
+
+ rc = ble_gap_conn_cancel();
+ return rc;
+}
+
+int
+btshell_term_conn(uint16_t conn_handle, uint8_t reason)
+{
+ int rc;
+
+ rc = ble_gap_terminate(conn_handle, reason);
+ return rc;
+}
+
+int
+btshell_wl_set(ble_addr_t *addrs, int addrs_count)
+{
+ int rc;
+
+ rc = ble_gap_wl_set(addrs, addrs_count);
+ return rc;
+}
+
+int
+btshell_scan(uint8_t own_addr_type, int32_t duration_ms,
+ const struct ble_gap_disc_params *disc_params, void *cb_args)
+{
+ int rc;
+
+ rc = ble_gap_disc(own_addr_type, duration_ms, disc_params,
+ btshell_gap_event, cb_args);
+ return rc;
+}
+
+int
+btshell_ext_scan(uint8_t own_addr_type, uint16_t duration, uint16_t period,
+ uint8_t filter_duplicates, uint8_t filter_policy,
+ uint8_t limited,
+ const struct ble_gap_ext_disc_params *uncoded_params,
+ const struct ble_gap_ext_disc_params *coded_params,
+ void *cb_args)
+{
+#if !MYNEWT_VAL(BLE_EXT_ADV)
+ console_printf("BLE extended advertising not supported.");
+ console_printf(" Configure nimble host to enable it\n");
+ return 0;
+#else
+ int rc;
+
+ rc = ble_gap_ext_disc(own_addr_type, duration, period, filter_duplicates,
+ filter_policy, limited, uncoded_params, coded_params,
+ btshell_gap_event, cb_args);
+ return rc;
+#endif
+}
+
+int
+btshell_scan_cancel(void)
+{
+ int rc;
+
+ rc = ble_gap_disc_cancel();
+ return rc;
+}
+
+int
+btshell_update_conn(uint16_t conn_handle, struct ble_gap_upd_params *params)
+{
+ int rc;
+
+ rc = ble_gap_update_params(conn_handle, params);
+ return rc;
+}
+
+void
+btshell_notify(uint16_t attr_handle)
+{
+ ble_gatts_chr_updated(attr_handle);
+}
+
+int
+btshell_datalen(uint16_t conn_handle, uint16_t tx_octets, uint16_t tx_time)
+{
+ int rc;
+
+ rc = ble_hs_hci_util_set_data_len(conn_handle, tx_octets, tx_time);
+ return rc;
+}
+
+int
+btshell_l2cap_update(uint16_t conn_handle,
+ struct ble_l2cap_sig_update_params *params)
+{
+ int rc;
+
+ rc = ble_l2cap_sig_update(conn_handle, params, btshell_on_l2cap_update,
+ NULL);
+ return rc;
+}
+
+int
+btshell_sec_pair(uint16_t conn_handle)
+{
+#if !NIMBLE_BLE_SM
+ return BLE_HS_ENOTSUP;
+#endif
+
+ int rc;
+
+ rc = ble_gap_pair_initiate(conn_handle);
+ return rc;
+}
+
+int
+btshell_sec_unpair(ble_addr_t *peer_addr)
+{
+#if !NIMBLE_BLE_SM
+ return BLE_HS_ENOTSUP;
+#endif
+
+ int rc;
+
+ rc = ble_gap_unpair(peer_addr);
+ return rc;
+}
+
+int
+btshell_sec_start(uint16_t conn_handle)
+{
+#if !NIMBLE_BLE_SM
+ return BLE_HS_ENOTSUP;
+#endif
+
+ int rc;
+
+ rc = ble_gap_security_initiate(conn_handle);
+ return rc;
+}
+
+int
+btshell_sec_restart(uint16_t conn_handle,
+ uint8_t key_size,
+ uint8_t *ltk,
+ uint16_t ediv,
+ uint64_t rand_val,
+ int auth)
+{
+#if !NIMBLE_BLE_SM
+ return BLE_HS_ENOTSUP;
+#endif
+
+ struct ble_store_value_sec value_sec;
+ struct ble_store_key_sec key_sec;
+ struct ble_gap_conn_desc desc;
+ ble_hs_conn_flags_t conn_flags;
+ int rc;
+
+ if (ltk == NULL) {
+ /* The user is requesting a store lookup. */
+ rc = ble_gap_conn_find(conn_handle, &desc);
+ if (rc != 0) {
+ return rc;
+ }
+
+ memset(&key_sec, 0, sizeof key_sec);
+ key_sec.peer_addr = desc.peer_id_addr;
+
+ rc = ble_hs_atomic_conn_flags(conn_handle, &conn_flags);
+ if (rc != 0) {
+ return rc;
+ }
+ if (conn_flags & BLE_HS_CONN_F_MASTER) {
+ rc = ble_store_read_peer_sec(&key_sec, &value_sec);
+ } else {
+ rc = ble_store_read_our_sec(&key_sec, &value_sec);
+ }
+ if (rc != 0) {
+ return rc;
+ }
+
+ ltk = value_sec.ltk;
+ key_size = value_sec.key_size;
+ ediv = value_sec.ediv;
+ rand_val = value_sec.rand_num;
+ auth = value_sec.authenticated;
+ }
+
+ rc = ble_gap_encryption_initiate(conn_handle, key_size, ltk,
+ ediv, rand_val, auth);
+ return rc;
+}
+
+/**
+ * Called to start transmitting 'num' packets at rate 'rate' of size 'size'
+ * to connection handle 'handle'
+ *
+ * @param handle
+ * @param len
+ * @param rate
+ * @param num
+ *
+ * @return int
+ */
+int
+btshell_tx_start(uint16_t conn_handle, uint16_t len, uint16_t rate, uint16_t num)
+{
+ /* Cannot be currently in a session */
+ if (num == 0) {
+ return 0;
+ }
+
+ /* Do not allow start if already in progress */
+ if (btshell_tx_data.tx_num != 0) {
+ return -1;
+ }
+
+ /* XXX: for now, must have contiguous mbuf space */
+ if ((len + 4) > MYNEWT_VAL_MSYS_1_BLOCK_SIZE) {
+ return -2;
+ }
+
+ btshell_tx_data.tx_num = num;
+ btshell_tx_data.tx_num_requested = num;
+ btshell_tx_data.tx_rate = rate;
+ btshell_tx_data.tx_len = len;
+ btshell_tx_data.tx_conn_handle = conn_handle;
+
+ ble_hs_lock();
+ btshell_tx_data.conn = ble_hs_conn_find(conn_handle);
+ ble_hs_unlock();
+
+ if (!btshell_tx_data.conn) {
+ console_printf("Could not find ble_hs_conn for handle: %d\n",
+ conn_handle);
+ return -1;
+ }
+
+ os_callout_reset(&btshell_tx_timer, 0);
+
+ return 0;
+}
+
+void
+btshell_tx_stop(void)
+{
+ os_callout_stop(&btshell_tx_timer);
+ btshell_tx_data.tx_num = 0;
+}
+
+int
+btshell_rssi(uint16_t conn_handle, int8_t *out_rssi)
+{
+ int rc;
+
+ rc = ble_gap_conn_rssi(conn_handle, out_rssi);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+static void
+btshell_on_reset(int reason)
+{
+ console_printf("Error: Resetting state; reason=%d\n", reason);
+}
+
+static void
+btshell_on_sync(void)
+{
+#if MYNEWT_VAL(BLE_SM_SC)
+ int rc;
+
+ rc = ble_sm_sc_oob_generate_data(&oob_data_local);
+ if (rc) {
+ console_printf("Error: generating oob data; reason=%d\n", rc);
+ return;
+ }
+#endif
+
+ console_printf("Host and controller synced\n");
+}
+
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
+
+static int
+btshell_l2cap_coc_add(uint16_t conn_handle, struct ble_l2cap_chan *chan)
+{
+ struct btshell_conn *conn;
+ struct btshell_l2cap_coc *coc;
+ struct btshell_l2cap_coc *prev, *cur;
+
+ conn = btshell_conn_find(conn_handle);
+ assert(conn != NULL);
+
+ coc = os_memblock_get(&btshell_coc_conn_pool);
+ if (!coc) {
+ return ENOMEM;
+ }
+
+ coc->chan = chan;
+
+ prev = NULL;
+ SLIST_FOREACH(cur, &conn->coc_list, next) {
+ prev = cur;
+ }
+
+ if (prev == NULL) {
+ SLIST_INSERT_HEAD(&conn->coc_list, coc, next);
+ } else {
+ SLIST_INSERT_AFTER(prev, coc, next);
+ }
+
+ return 0;
+}
+
+static void
+btshell_l2cap_coc_remove(uint16_t conn_handle, struct ble_l2cap_chan *chan)
+{
+ struct btshell_conn *conn;
+ struct btshell_l2cap_coc *coc;
+ struct btshell_l2cap_coc *cur;
+
+ conn = btshell_conn_find(conn_handle);
+ assert(conn != NULL);
+
+ coc = NULL;
+ SLIST_FOREACH(cur, &conn->coc_list, next) {
+ if (cur->chan == chan) {
+ coc = cur;
+ break;
+ }
+ }
+
+ if (!coc) {
+ return;
+ }
+
+ SLIST_REMOVE(&conn->coc_list, coc, btshell_l2cap_coc, next);
+ os_memblock_put(&btshell_coc_conn_pool, coc);
+}
+
+static void
+btshell_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu)
+{
+ console_printf("LE CoC SDU received, chan: 0x%08lx, data len %d\n",
+ (uint32_t) chan, OS_MBUF_PKTLEN(sdu));
+
+ os_mbuf_free_chain(sdu);
+ sdu = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
+ assert(sdu != NULL);
+
+ if (ble_l2cap_recv_ready(chan, sdu) != 0) {
+ assert(0);
+ }
+}
+
+static int
+btshell_l2cap_coc_accept(uint16_t conn_handle, uint16_t peer_mtu,
+ struct ble_l2cap_chan *chan)
+{
+ struct os_mbuf *sdu_rx;
+
+ console_printf("LE CoC accepting, chan: 0x%08lx, peer_mtu %d\n",
+ (uint32_t) chan, peer_mtu);
+
+ sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
+ if (!sdu_rx) {
+ return BLE_HS_ENOMEM;
+ }
+
+ return ble_l2cap_recv_ready(chan, sdu_rx);
+}
+
+static void
+btshell_l2cap_coc_unstalled(uint16_t conn_handle, struct ble_l2cap_chan *chan)
+{
+ struct btshell_conn *conn;
+ struct btshell_l2cap_coc *coc;
+ struct btshell_l2cap_coc *cur;
+
+ conn = btshell_conn_find(conn_handle);
+ assert(conn != NULL);
+
+ coc = NULL;
+ SLIST_FOREACH(cur, &conn->coc_list, next) {
+ if (cur->chan == chan) {
+ coc = cur;
+ break;
+ }
+ }
+
+ if (!coc) {
+ return;
+ }
+
+ coc->stalled = false;
+}
+
+static int
+btshell_l2cap_event(struct ble_l2cap_event *event, void *arg)
+{
+ int accept_response;
+ struct ble_l2cap_chan_info chan_info;
+
+ switch(event->type) {
+ case BLE_L2CAP_EVENT_COC_CONNECTED:
+ if (event->connect.status) {
+ console_printf("LE COC error: %d\n", event->connect.status);
+ return 0;
+ }
+
+ if (ble_l2cap_get_chan_info(event->connect.chan, &chan_info)) {
+ assert(0);
+ }
+
+ console_printf("LE COC connected, conn: %d, chan: %p, psm: 0x%02x, scid: 0x%04x, "
+ "dcid: 0x%04x, our_mps: %d, our_mtu: %d, peer_mps: %d, peer_mtu: %d\n",
+ event->connect.conn_handle, event->connect.chan,
+ chan_info.psm, chan_info.scid, chan_info.dcid,
+ chan_info.our_l2cap_mtu, chan_info.our_coc_mtu, chan_info.peer_l2cap_mtu, chan_info.peer_coc_mtu);
+
+ btshell_l2cap_coc_add(event->connect.conn_handle,
+ event->connect.chan);
+
+ return 0;
+ case BLE_L2CAP_EVENT_COC_DISCONNECTED:
+ console_printf("LE CoC disconnected, chan: %p\n",
+ event->disconnect.chan);
+
+ btshell_l2cap_coc_remove(event->disconnect.conn_handle,
+ event->disconnect.chan);
+ return 0;
+ case BLE_L2CAP_EVENT_COC_ACCEPT:
+ accept_response = PTR_TO_INT(arg);
+ if (accept_response) {
+ return accept_response;
+ }
+
+ return btshell_l2cap_coc_accept(event->accept.conn_handle,
+ event->accept.peer_sdu_size,
+ event->accept.chan);
+
+ case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
+ btshell_l2cap_coc_recv(event->receive.chan, event->receive.sdu_rx);
+ return 0;
+ case BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED:
+
+ if (ble_l2cap_get_chan_info(event->reconfigured.chan, &chan_info)) {
+ assert(0);
+ }
+
+ console_printf("LE CoC reconfigure completed status 0x%02x," \
+ "chan: %p\n",
+ event->reconfigured.status,
+ event->reconfigured.chan);
+
+ if (event->reconfigured.status == 0) {
+ console_printf("\t our_mps: %d our_mtu %d\n", chan_info.our_l2cap_mtu, chan_info.our_coc_mtu);
+ }
+ return 0;
+ case BLE_L2CAP_EVENT_COC_PEER_RECONFIGURED:
+
+ if (ble_l2cap_get_chan_info(event->reconfigured.chan, &chan_info)) {
+ assert(0);
+ }
+
+ console_printf("LE CoC peer reconfigured status 0x%02x," \
+ "chan: %p\n",
+ event->reconfigured.status,
+ event->reconfigured.chan);
+
+ if (event->reconfigured.status == 0) {
+ console_printf("\t peer_mps: %d peer_mtu %d\n", chan_info.peer_l2cap_mtu, chan_info.peer_coc_mtu);
+ }
+
+ return 0;
+ case BLE_L2CAP_EVENT_COC_TX_UNSTALLED:
+ console_printf("L2CAP CoC channel %p unstalled, last sdu sent with err=0x%02x\n",
+ event->tx_unstalled.chan, event->tx_unstalled.status);
+ btshell_l2cap_coc_unstalled(event->tx_unstalled.conn_handle, event->tx_unstalled.chan);
+ return 0;
+ default:
+ return 0;
+ }
+}
+#endif
+
+int
+btshell_l2cap_create_srv(uint16_t psm, uint16_t mtu, int accept_response)
+{
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0
+ console_printf("BLE L2CAP LE COC not supported.");
+ console_printf(" Configure nimble host to enable it\n");
+ return 0;
+#else
+
+ if (mtu == 0 || mtu > BTSHELL_COC_MTU) {
+ mtu = BTSHELL_COC_MTU;
+ }
+
+ return ble_l2cap_create_server(psm, mtu, btshell_l2cap_event,
+ INT_TO_PTR(accept_response));
+#endif
+}
+
+int
+btshell_l2cap_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu, uint8_t num)
+{
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0
+ console_printf("BLE L2CAP LE COC not supported.");
+ console_printf(" Configure nimble host to enable it\n");
+ return 0;
+#else
+
+ struct os_mbuf *sdu_rx[num];
+ int i;
+
+ if (mtu == 0 || mtu > BTSHELL_COC_MTU) {
+ mtu = BTSHELL_COC_MTU;
+ }
+
+ console_printf("L2CAP CoC MTU: %d, max available %d\n", mtu, BTSHELL_COC_MTU);
+
+ for (i = 0; i < num; i++) {
+ sdu_rx[i] = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
+ assert(sdu_rx != NULL);
+ }
+
+ if (num == 1) {
+ return ble_l2cap_connect(conn_handle, psm, mtu, sdu_rx[0],
+ btshell_l2cap_event, NULL);
+ }
+
+ return ble_l2cap_enhanced_connect(conn_handle, psm, mtu,
+ num, sdu_rx,btshell_l2cap_event, NULL);
+#endif
+}
+
+int
+btshell_l2cap_disconnect(uint16_t conn_handle, uint16_t idx)
+{
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0
+ console_printf("BLE L2CAP LE COC not supported.");
+ console_printf(" Configure nimble host to enable it\n");
+ return 0;
+#else
+
+ struct btshell_conn *conn;
+ struct btshell_l2cap_coc *coc;
+ int i;
+ int rc = 0;
+
+ conn = btshell_conn_find(conn_handle);
+ assert(conn != NULL);
+
+ i = 0;
+ SLIST_FOREACH(coc, &conn->coc_list, next) {
+ if (i == idx) {
+ break;
+ }
+ i++;
+ }
+ assert(coc != NULL);
+
+ rc = ble_l2cap_disconnect(coc->chan);
+ if (rc) {
+ console_printf("Could not disconnect channel rc=%d\n", rc);
+ }
+
+ return rc;
+#endif
+}
+
+int
+btshell_l2cap_reconfig(uint16_t conn_handle, uint16_t mtu,
+ uint8_t num, uint8_t idxs[])
+{
+ struct btshell_conn *conn;
+ struct btshell_l2cap_coc *coc;
+ struct ble_l2cap_chan * chans[5] = {0};
+ int i, j;
+ int cnt;
+
+ conn = btshell_conn_find(conn_handle);
+ if (conn == NULL) {
+ console_printf("conn=%d does not exist\n", conn_handle);
+ return 0;
+ }
+
+ i = 0;
+ j = 0;
+ cnt = 0;
+ SLIST_FOREACH(coc, &conn->coc_list, next) {
+ for (i = 0; i < num; i++) {
+ if (idxs[i] == j) {
+ chans[cnt] = coc->chan;
+ cnt++;
+ break;
+ }
+ }
+ j++;
+ }
+
+ if (cnt != num) {
+ console_printf("Missing coc? (%d!=%d)\n", num, cnt);
+ return BLE_HS_EINVAL;
+ }
+
+ return ble_l2cap_reconfig(chans, cnt, mtu);
+}
+
+int
+btshell_l2cap_send(uint16_t conn_handle, uint16_t idx, uint16_t bytes)
+{
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0
+ console_printf("BLE L2CAP LE COC not supported.");
+ console_printf(" Configure nimble host to enable it\n");
+ return 0;
+#else
+
+ struct btshell_conn *conn;
+ struct btshell_l2cap_coc *coc;
+ struct os_mbuf *sdu_tx;
+ uint8_t b[] = {0x00, 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88, 0x99};
+ int i;
+ int rc;
+
+ console_printf("conn=%d, idx=%d, bytes=%d\n", conn_handle, idx, bytes);
+
+ conn = btshell_conn_find(conn_handle);
+ if (conn == NULL) {
+ console_printf("conn=%d does not exist\n", conn_handle);
+ return 0;
+ }
+
+ i = 0;
+ SLIST_FOREACH(coc, &conn->coc_list, next) {
+ if (i == idx) {
+ break;
+ }
+ i++;
+ }
+ if (coc == NULL) {
+ console_printf("Are you sure your channel exist?\n");
+ return 0;
+ }
+
+ if (coc->stalled) {
+ console_printf("Channel is stalled, wait ...\n");
+ return 0;
+ }
+
+ sdu_tx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
+ if (sdu_tx == NULL) {
+ console_printf("No memory in the test sdu pool\n");
+ return 0;
+ }
+
+ /* For the testing purpose we fill up buffer with known data, easy
+ * to validate on other side. In this loop we add as many full chunks as we
+ * can
+ */
+ for (i = 0; i < bytes / sizeof(b); i++) {
+ rc = os_mbuf_append(sdu_tx, b, sizeof(b));
+ if (rc) {
+ console_printf("Cannot append data %i !\n", i);
+ os_mbuf_free_chain(sdu_tx);
+ return rc;
+ }
+ }
+
+ /* Here we add the rest < sizeof(b) */
+ rc = os_mbuf_append(sdu_tx, b, bytes - (sizeof(b) * i));
+ if (rc) {
+ console_printf("Cannot append data %i !\n", i);
+ os_mbuf_free_chain(sdu_tx);
+ return rc;
+ }
+
+ rc = ble_l2cap_send(coc->chan, sdu_tx);
+ if (rc) {
+ if (rc == BLE_HS_ESTALLED) {
+ console_printf("CoC module is stalled with data. Wait for unstalled \n");
+ coc->stalled = true;
+ } else {
+ console_printf("Could not send data rc=%d\n", rc);
+ }
+ os_mbuf_free_chain(sdu_tx);
+ }
+
+ return rc;
+
+#endif
+}
+
+static void
+btshell_init_ext_adv_restart(void)
+{
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ int i;
+
+ for (i = 0; i < BLE_ADV_INSTANCES; ++i) {
+ ext_adv_restart[i].conn_handle = BLE_HS_CONN_HANDLE_NONE;
+ }
+#endif
+}
+
+/**
+ * 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(int argc, char **argv)
+{
+ int rc;
+
+#ifdef ARCH_sim
+ mcu_sim_parse_args(argc, argv);
+#endif
+
+ /* Initialize OS */
+ sysinit();
+
+ /* Initialize some application specific memory pools. */
+ rc = os_mempool_init(&btshell_svc_pool, BTSHELL_MAX_SVCS,
+ sizeof (struct btshell_svc), btshell_svc_mem,
+ "btshell_svc_pool");
+ assert(rc == 0);
+
+ rc = os_mempool_init(&btshell_chr_pool, BTSHELL_MAX_CHRS,
+ sizeof (struct btshell_chr), btshell_chr_mem,
+ "btshell_chr_pool");
+ assert(rc == 0);
+
+ rc = os_mempool_init(&btshell_dsc_pool, BTSHELL_MAX_DSCS,
+ sizeof (struct btshell_dsc), btshell_dsc_mem,
+ "btshell_dsc_pool");
+ assert(rc == 0);
+
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
+ /* For testing we want to support all the available channels */
+ rc = os_mempool_init(&sdu_coc_mbuf_mempool, BTSHELL_COC_BUF_COUNT,
+ BTSHELL_COC_MTU, btshell_sdu_coc_mem,
+ "btshell_coc_sdu_pool");
+ assert(rc == 0);
+
+ rc = os_mbuf_pool_init(&sdu_os_mbuf_pool, &sdu_coc_mbuf_mempool,
+ BTSHELL_COC_MTU, BTSHELL_COC_BUF_COUNT);
+ assert(rc == 0);
+
+ rc = os_mempool_init(&btshell_coc_conn_pool,
+ MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM),
+ sizeof (struct btshell_l2cap_coc), btshell_coc_conn_mem,
+ "btshell_coc_conn_pool");
+ assert(rc == 0);
+#endif
+
+ /* Initialize the NimBLE host configuration. */
+ ble_hs_cfg.reset_cb = btshell_on_reset;
+ ble_hs_cfg.sync_cb = btshell_on_sync;
+ ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
+ ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
+
+ rc = gatt_svr_init();
+ assert(rc == 0);
+
+ cmd_init();
+
+ /* Set the default device name. */
+ rc = ble_svc_gap_device_name_set("nimble-btshell");
+ assert(rc == 0);
+
+ /* Create a callout (timer). This callout is used by the "tx" btshell
+ * command to repeatedly send packets of sequential data bytes.
+ */
+ os_callout_init(&btshell_tx_timer, os_eventq_dflt_get(),
+ btshell_tx_timer_cb, NULL);
+
+ btshell_init_ext_adv_restart();
+
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+ /* os start should never return. If it does, this should be an error */
+ assert(0);
+
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/btshell/src/misc.c b/src/libs/mynewt-nimble/apps/btshell/src/misc.c
new file mode 100644
index 00000000..e100eb79
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/btshell/src/misc.c
@@ -0,0 +1,163 @@
+/*
+ * 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 "console/console.h"
+#include "host/ble_uuid.h"
+#include "host/ble_gap.h"
+
+#include "btshell.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++) {
+ console_printf("%s0x%02x", i != 0 ? ":" : "", bytes[i]);
+ }
+}
+
+void
+print_mbuf(const struct os_mbuf *om)
+{
+ int colon;
+
+ colon = 0;
+ while (om != NULL) {
+ if (colon) {
+ console_printf(":");
+ } else {
+ colon = 1;
+ }
+ print_bytes(om->om_data, om->om_len);
+ om = SLIST_NEXT(om, om_next);
+ }
+}
+
+void
+print_addr(const void *addr)
+{
+ const uint8_t *u8p;
+
+ u8p = addr;
+ console_printf("%02x:%02x:%02x:%02x:%02x:%02x",
+ u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
+}
+
+void
+print_uuid(const ble_uuid_t *uuid)
+{
+ char buf[BLE_UUID_STR_LEN];
+
+ ble_uuid_to_str(uuid, buf);
+
+ console_printf("%s", buf);
+}
+
+int
+svc_is_empty(const struct btshell_svc *svc)
+{
+ return svc->svc.end_handle <= svc->svc.start_handle;
+}
+
+uint16_t
+chr_end_handle(const struct btshell_svc *svc, const struct btshell_chr *chr)
+{
+ const struct btshell_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 btshell_svc *svc, const struct btshell_chr *chr)
+{
+ return chr_end_handle(svc, chr) <= chr->chr.val_handle;
+}
+
+void
+print_conn_desc(const struct ble_gap_conn_desc *desc)
+{
+ console_printf("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);
+ console_printf(" our_id_addr_type=%d our_id_addr=",
+ desc->our_id_addr.type);
+ print_addr(desc->our_id_addr.val);
+ console_printf(" peer_ota_addr_type=%d peer_ota_addr=",
+ desc->peer_ota_addr.type);
+ print_addr(desc->peer_ota_addr.val);
+ console_printf(" peer_id_addr_type=%d peer_id_addr=",
+ desc->peer_id_addr.type);
+ print_addr(desc->peer_id_addr.val);
+ console_printf(" conn_itvl=%d conn_latency=%d supervision_timeout=%d"
+ " key_size=%d encrypted=%d authenticated=%d bonded=%d\n",
+ desc->conn_itvl, desc->conn_latency,
+ desc->supervision_timeout,
+ desc->sec_state.key_size,
+ desc->sec_state.encrypted,
+ desc->sec_state.authenticated,
+ desc->sec_state.bonded);
+}
+
+static void
+print_dsc(struct btshell_dsc *dsc)
+{
+ console_printf(" dsc_handle=%d uuid=", dsc->dsc.handle);
+ print_uuid(&dsc->dsc.uuid.u);
+ console_printf("\n");
+}
+
+static void
+print_chr(struct btshell_chr *chr)
+{
+ struct btshell_dsc *dsc;
+
+ console_printf(" def_handle=%d val_handle=%d properties=0x%02x "
+ "uuid=", chr->chr.def_handle, chr->chr.val_handle,
+ chr->chr.properties);
+ print_uuid(&chr->chr.uuid.u);
+ console_printf("\n");
+
+ SLIST_FOREACH(dsc, &chr->dscs, next) {
+ print_dsc(dsc);
+ }
+}
+
+void
+print_svc(struct btshell_svc *svc)
+{
+ struct btshell_chr *chr;
+
+ console_printf(" start=%d end=%d uuid=", svc->svc.start_handle,
+ svc->svc.end_handle);
+ print_uuid(&svc->svc.uuid.u);
+ console_printf("\n");
+
+ SLIST_FOREACH(chr, &svc->chrs, next) {
+ print_chr(chr);
+ }
+}
diff --git a/src/libs/mynewt-nimble/apps/btshell/src/parse.c b/src/libs/mynewt-nimble/apps/btshell/src/parse.c
new file mode 100644
index 00000000..d8018c5c
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/btshell/src/parse.c
@@ -0,0 +1,734 @@
+/*
+ * 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 <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+#include "console/console.h"
+#include "host/ble_hs.h"
+#include "host/ble_uuid.h"
+#include "host/ble_eddystone.h"
+#include "cmd.h"
+#include "btshell.h"
+
+#define CMD_MAX_ARGS 16
+
+static char *cmd_args[CMD_MAX_ARGS][2];
+static int cmd_num_args;
+
+int
+parse_arg_find_idx(const char *key)
+{
+ int i;
+
+ for (i = 0; i < cmd_num_args; i++) {
+ if (strcmp(cmd_args[i][0], key) == 0) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+char *
+parse_arg_peek(const char *key)
+{
+ int i;
+
+ for (i = 0; i < cmd_num_args; i++) {
+ if (strcmp(cmd_args[i][0], key) == 0) {
+ return cmd_args[i][1];
+ }
+ }
+
+ return NULL;
+}
+
+char *
+parse_arg_extract(const char *key)
+{
+ int i;
+
+ for (i = 0; i < cmd_num_args; i++) {
+ if (strcmp(cmd_args[i][0], key) == 0) {
+ /* Erase parameter. */
+ cmd_args[i][0][0] = '\0';
+
+ return cmd_args[i][1];
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Determines which number base to use when parsing the specified numeric
+ * string. This just avoids base '0' so that numbers don't get interpreted as
+ * octal.
+ */
+static int
+parse_arg_long_base(char *sval)
+{
+ if (sval[0] == '0' && sval[1] == 'x') {
+ return 0;
+ } else {
+ return 10;
+ }
+}
+
+long
+parse_long_bounds(char *sval, long min, long max, int *out_status)
+{
+ char *endptr;
+ long lval;
+
+ lval = strtol(sval, &endptr, parse_arg_long_base(sval));
+ if (sval[0] != '\0' && *endptr == '\0' &&
+ lval >= min && lval <= max) {
+
+ *out_status = 0;
+ return lval;
+ }
+
+ *out_status = EINVAL;
+ return 0;
+}
+
+long
+parse_arg_long_bounds_peek(char *name, long min, long max, int *out_status)
+{
+ char *sval;
+
+ sval = parse_arg_peek(name);
+ if (sval == NULL) {
+ *out_status = ENOENT;
+ return 0;
+ }
+ return parse_long_bounds(sval, min, max, out_status);
+}
+
+long
+parse_arg_long_bounds(char *name, long min, long max, int *out_status)
+{
+ char *sval;
+
+ sval = parse_arg_extract(name);
+ if (sval == NULL) {
+ *out_status = ENOENT;
+ return 0;
+ }
+ return parse_long_bounds(sval, min, max, out_status);
+}
+
+long
+parse_arg_long_bounds_dflt(char *name, long min, long max,
+ long dflt, int *out_status)
+{
+ long val;
+ int rc;
+
+ val = parse_arg_long_bounds(name, min, max, &rc);
+ if (rc == ENOENT) {
+ rc = 0;
+ val = dflt;
+ }
+
+ *out_status = rc;
+
+ return val;
+}
+
+uint64_t
+parse_arg_uint64_bounds(char *name, uint64_t min, uint64_t max, int *out_status)
+{
+ char *endptr;
+ char *sval;
+ uint64_t lval;
+
+ sval = parse_arg_extract(name);
+ if (sval == NULL) {
+ *out_status = ENOENT;
+ return 0;
+ }
+
+ lval = strtoull(sval, &endptr, parse_arg_long_base(sval));
+ if (sval[0] != '\0' && *endptr == '\0' &&
+ lval >= min && lval <= max) {
+
+ *out_status = 0;
+ return lval;
+ }
+
+ *out_status = EINVAL;
+ return 0;
+}
+
+long
+parse_arg_long(char *name, int *out_status)
+{
+ return parse_arg_long_bounds(name, LONG_MIN, LONG_MAX, out_status);
+}
+
+uint8_t
+parse_arg_bool(char *name, int *out_status)
+{
+ return parse_arg_long_bounds(name, 0, 1, out_status);
+}
+
+uint8_t
+parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status)
+{
+ return parse_arg_long_bounds_dflt(name, 0, 1, dflt, out_status);
+}
+
+uint8_t
+parse_arg_uint8(char *name, int *out_status)
+{
+ return parse_arg_long_bounds(name, 0, UINT8_MAX, out_status);
+}
+
+uint16_t
+parse_arg_uint16(char *name, int *out_status)
+{
+ return parse_arg_long_bounds(name, 0, UINT16_MAX, out_status);
+}
+
+uint16_t
+parse_arg_uint16_peek(char *name, int *out_status)
+{
+ return parse_arg_long_bounds_peek(name, 0, UINT16_MAX, out_status);
+}
+
+uint32_t
+parse_arg_uint32(char *name, int *out_status)
+{
+ return parse_arg_uint64_bounds(name, 0, UINT32_MAX, out_status);
+}
+
+uint64_t
+parse_arg_uint64(char *name, int *out_status)
+{
+ return parse_arg_uint64_bounds(name, 0, UINT64_MAX, out_status);
+}
+
+uint8_t
+parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status)
+{
+ uint8_t val;
+ int rc;
+
+ val = parse_arg_uint8(name, &rc);
+ if (rc == ENOENT) {
+ val = dflt;
+ rc = 0;
+ }
+
+ *out_status = rc;
+ return val;
+}
+
+uint16_t
+parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status)
+{
+ uint16_t val;
+ int rc;
+
+ val = parse_arg_uint16(name, &rc);
+ if (rc == ENOENT) {
+ val = dflt;
+ rc = 0;
+ }
+
+ *out_status = rc;
+ return val;
+}
+
+uint32_t
+parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status)
+{
+ uint32_t val;
+ int rc;
+
+ val = parse_arg_uint32(name, &rc);
+ if (rc == ENOENT) {
+ val = dflt;
+ rc = 0;
+ }
+
+ *out_status = rc;
+ return val;
+}
+
+static uint32_t
+parse_time_unit_mult(const char *str)
+{
+ if (!strcasecmp(str, "us")) {
+ return 1;
+ } else if (!strcasecmp(str, "ms")) {
+ return 1000;
+ } else if (!strcasecmp(str, "s")) {
+ return 1000000;
+ }
+
+ return 0;
+}
+
+static uint32_t
+parse_time_us(const char *str, int *out_status)
+{
+ uint32_t val = 0;
+ uint32_t val_div = 1;
+ uint32_t val_mult = 1;
+ uint32_t val_us;
+
+ while (isdigit(*str)) {
+ val *= 10;
+ val += *str - '0';
+ str++;
+ }
+
+ if (*str == '.') {
+ str++;
+ while (isdigit(*str)) {
+ val *= 10;
+ val += *str - '0';
+ val_div *= 10;
+ str++;
+ }
+ }
+
+ val_mult = parse_time_unit_mult(str);
+ if (val_mult == 0) {
+ *out_status = EINVAL;
+ return 0;
+ }
+
+ if (val_mult > val_div) {
+ val_us = val * (val_mult / val_div);
+ } else {
+ val_us = val * (val_div / val_mult);
+ }
+
+ *out_status = 0;
+
+ return val_us;
+}
+
+uint32_t
+parse_arg_time_dflt(char *name, int step_us, uint32_t dflt, int *out_status)
+{
+ const char *arg;
+ uint32_t val;
+ int rc;
+
+ arg = parse_arg_peek(name);
+ if (!arg) {
+ *out_status = 0;
+ return dflt;
+ }
+
+ val = parse_time_us(arg, &rc);
+ if (rc) {
+ val = parse_arg_uint32(name, &rc);
+ if (rc == ENOENT) {
+ *out_status = 0;
+ return dflt;
+ }
+ } else {
+ val /= step_us;
+ parse_arg_extract(name);
+ }
+
+ *out_status = rc;
+ return val;
+}
+
+const struct kv_pair *
+parse_kv_find(const struct kv_pair *kvs, char *name)
+{
+ const struct kv_pair *kv;
+ int i;
+
+ for (i = 0; kvs[i].key != NULL; i++) {
+ kv = kvs + i;
+ if (strcmp(name, kv->key) == 0) {
+ return kv;
+ }
+ }
+
+ return NULL;
+}
+
+int
+parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status)
+{
+ const struct kv_pair *kv;
+ char *sval;
+
+ sval = parse_arg_extract(name);
+ if (sval == NULL) {
+ *out_status = ENOENT;
+ return -1;
+ }
+
+ kv = parse_kv_find(kvs, sval);
+ if (kv == NULL) {
+ *out_status = EINVAL;
+ return -1;
+ }
+
+ *out_status = 0;
+ return kv->val;
+}
+
+int
+parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val,
+ int *out_status)
+{
+ int val;
+ int rc;
+
+ val = parse_arg_kv(name, kvs, &rc);
+ if (rc == ENOENT) {
+ rc = 0;
+ val = def_val;
+ }
+
+ *out_status = rc;
+
+ return val;
+}
+
+
+static int
+parse_arg_byte_stream_delim(char *sval, char *delims, int max_len,
+ uint8_t *dst, int *out_len)
+{
+ unsigned long ul;
+ char *endptr;
+ char *token;
+ int i;
+
+ i = 0;
+ for (token = strtok(sval, delims);
+ token != NULL;
+ token = strtok(NULL, delims)) {
+
+ if (i >= max_len) {
+ return EINVAL;
+ }
+
+ ul = strtoul(token, &endptr, 16);
+ if (sval[0] == '\0' || *endptr != '\0' || ul > UINT8_MAX) {
+ return -1;
+ }
+
+ dst[i] = ul;
+ i++;
+ }
+
+ *out_len = i;
+
+ return 0;
+}
+
+int
+parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len)
+{
+ char *sval;
+
+ sval = parse_arg_extract(name);
+ if (sval == NULL) {
+ return ENOENT;
+ }
+
+ return parse_arg_byte_stream_delim(sval, ":-", max_len, dst, out_len);
+}
+
+int
+parse_arg_uint8_list_with_separator(char *name, char *separator, int max_len,
+ uint8_t *dst, int *out_len)
+{
+ char *sval;
+
+ sval = parse_arg_extract(name);
+ if (sval == NULL) {
+ return ENOENT;
+ }
+
+ return parse_arg_byte_stream_delim(sval, separator, max_len, dst, out_len);
+}
+
+int
+parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len)
+{
+ int actual_len;
+ int rc;
+
+ rc = parse_arg_byte_stream(name, len, dst, &actual_len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (actual_len != len) {
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+static void
+parse_reverse_bytes(uint8_t *bytes, int len)
+{
+ uint8_t tmp;
+ int i;
+
+ for (i = 0; i < len / 2; i++) {
+ tmp = bytes[i];
+ bytes[i] = bytes[len - i - 1];
+ bytes[len - i - 1] = tmp;
+ }
+}
+
+int
+parse_arg_mac(char *name, uint8_t *dst)
+{
+ int rc;
+
+ rc = parse_arg_byte_stream_exact_length(name, dst, 6);
+ if (rc != 0) {
+ return rc;
+ }
+
+ parse_reverse_bytes(dst, 6);
+
+ return 0;
+}
+
+int
+parse_arg_addr(char *name, ble_addr_t *addr)
+{
+ char *arg;
+ size_t len;
+ uint8_t addr_type;
+ bool addr_type_found;
+ int rc;
+
+ arg = parse_arg_peek(name);
+ if (!arg) {
+ return ENOENT;
+ }
+
+ len = strlen(arg);
+ if (len < 2) {
+ return EINVAL;
+ }
+
+ addr_type_found = false;
+ if ((arg[len - 2] == ':') || (arg[len - 2] == '-')) {
+ if (tolower(arg[len - 1]) == 'p') {
+ addr_type = BLE_ADDR_PUBLIC;
+ addr_type_found = true;
+ } else if (tolower(arg[len - 1]) == 'r') {
+ addr_type = BLE_ADDR_RANDOM;
+ addr_type_found = true;
+ }
+
+ if (addr_type_found) {
+ arg[len - 2] = '\0';
+ }
+}
+
+ rc = parse_arg_mac(name, addr->val);
+ if (rc != 0) {
+ return rc;
+ }
+
+ if (addr_type_found) {
+ addr->type = addr_type;
+ } else {
+ rc = EAGAIN;
+ }
+
+ return rc;
+}
+
+int
+parse_arg_uuid(char *str, ble_uuid_any_t *uuid)
+{
+ uint16_t uuid16;
+ uint8_t val[16];
+ int len;
+ int rc;
+
+ uuid16 = parse_arg_uint16_peek(str, &rc);
+ switch (rc) {
+ case ENOENT:
+ parse_arg_extract(str);
+ return ENOENT;
+
+ case 0:
+ len = 2;
+ val[0] = uuid16;
+ val[1] = uuid16 >> 8;
+ parse_arg_extract(str);
+ break;
+
+ default:
+ len = 16;
+ rc = parse_arg_byte_stream_exact_length(str, val, 16);
+ if (rc != 0) {
+ return EINVAL;
+ }
+ parse_reverse_bytes(val, 16);
+ break;
+ }
+
+ rc = ble_uuid_init_from_buf(uuid, val, len);
+ if (rc != 0) {
+ return EINVAL;
+ } else {
+ return 0;
+ }
+}
+
+int
+parse_arg_all(int argc, char **argv)
+{
+ char *key;
+ char *val;
+ int i;
+
+ cmd_num_args = 0;
+
+ for (i = 0; i < argc; i++) {
+ key = strtok(argv[i], "=");
+ val = strtok(NULL, "=");
+
+ if (key != NULL && val != NULL) {
+ if (strlen(key) == 0) {
+ console_printf("Error: invalid argument: %s\n", argv[i]);
+ return -1;
+ }
+
+ if (cmd_num_args >= CMD_MAX_ARGS) {
+ console_printf("Error: too many arguments");
+ return -1;
+ }
+
+ cmd_args[cmd_num_args][0] = key;
+ cmd_args[cmd_num_args][1] = val;
+ cmd_num_args++;
+ }
+ }
+
+ return 0;
+}
+
+int
+parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body,
+ uint8_t *out_body_len, uint8_t *out_suffix)
+{
+ static const struct {
+ char *s;
+ uint8_t scheme;
+ } schemes[] = {
+ { "http://www.", BLE_EDDYSTONE_URL_SCHEME_HTTP_WWW },
+ { "https://www.", BLE_EDDYSTONE_URL_SCHEME_HTTPS_WWW },
+ { "http://", BLE_EDDYSTONE_URL_SCHEME_HTTP },
+ { "https://", BLE_EDDYSTONE_URL_SCHEME_HTTPS },
+ };
+
+ static const struct {
+ char *s;
+ uint8_t code;
+ } suffixes[] = {
+ { ".com/", BLE_EDDYSTONE_URL_SUFFIX_COM_SLASH },
+ { ".org/", BLE_EDDYSTONE_URL_SUFFIX_ORG_SLASH },
+ { ".edu/", BLE_EDDYSTONE_URL_SUFFIX_EDU_SLASH },
+ { ".net/", BLE_EDDYSTONE_URL_SUFFIX_NET_SLASH },
+ { ".info/", BLE_EDDYSTONE_URL_SUFFIX_INFO_SLASH },
+ { ".biz/", BLE_EDDYSTONE_URL_SUFFIX_BIZ_SLASH },
+ { ".gov/", BLE_EDDYSTONE_URL_SUFFIX_GOV_SLASH },
+ { ".com", BLE_EDDYSTONE_URL_SUFFIX_COM },
+ { ".org", BLE_EDDYSTONE_URL_SUFFIX_ORG },
+ { ".edu", BLE_EDDYSTONE_URL_SUFFIX_EDU },
+ { ".net", BLE_EDDYSTONE_URL_SUFFIX_NET },
+ { ".info", BLE_EDDYSTONE_URL_SUFFIX_INFO },
+ { ".biz", BLE_EDDYSTONE_URL_SUFFIX_BIZ },
+ { ".gov", BLE_EDDYSTONE_URL_SUFFIX_GOV },
+ };
+
+ char *prefix;
+ char *suffix;
+ int full_url_len;
+ int prefix_len;
+ int suffix_len;
+ int suffix_idx;
+ int rc;
+ int i;
+
+ full_url_len = strlen(full_url);
+
+ rc = BLE_HS_EINVAL;
+ for (i = 0; i < sizeof schemes / sizeof schemes[0]; i++) {
+ prefix = schemes[i].s;
+ prefix_len = strlen(schemes[i].s);
+
+ if (full_url_len >= prefix_len &&
+ memcmp(full_url, prefix, prefix_len) == 0) {
+
+ *out_scheme = i;
+ rc = 0;
+ break;
+ }
+ }
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = BLE_HS_EINVAL;
+ for (i = 0; i < sizeof suffixes / sizeof suffixes[0]; i++) {
+ suffix = suffixes[i].s;
+ suffix_len = strlen(suffixes[i].s);
+
+ suffix_idx = full_url_len - suffix_len;
+ if (suffix_idx >= prefix_len &&
+ memcmp(full_url + suffix_idx, suffix, suffix_len) == 0) {
+
+ *out_suffix = i;
+ rc = 0;
+ break;
+ }
+ }
+ if (rc != 0) {
+ *out_suffix = BLE_EDDYSTONE_URL_SUFFIX_NONE;
+ *out_body_len = full_url_len - prefix_len;
+ } else {
+ *out_body_len = full_url_len - prefix_len - suffix_len;
+ }
+
+ memcpy(out_body, full_url + prefix_len, *out_body_len);
+
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/btshell/syscfg.yml b/src/libs/mynewt-nimble/apps/btshell/syscfg.yml
new file mode 100644
index 00000000..9ebf9d89
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/btshell/syscfg.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.
+#
+
+syscfg.defs:
+ BTSHELL_ANS:
+ description: Include support for the alert notification service.
+ value: 1
+
+syscfg.vals:
+ # Enable the shell task.
+ SHELL_TASK: 1
+
+ # Set log level to info (disable debug logging).
+ LOG_LEVEL: 1
+
+ # Disable security manager (pairing and bonding).
+ BLE_SM_LEGACY: 0
+ BLE_SM_SC: 0
+
+ # Default task settings
+ OS_MAIN_STACK_SIZE: 512
+
+ # SMP is not supported in this app, so disable smp-over-shell.
+ SHELL_MGMT: 0
+
+syscfg.vals.BLE_MESH:
+ MSYS_1_BLOCK_COUNT: 16
diff --git a/src/libs/mynewt-nimble/apps/bttester/README b/src/libs/mynewt-nimble/apps/bttester/README
new file mode 100644
index 00000000..29db2eba
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/README
@@ -0,0 +1,14 @@
+Title: Bluetooth tester application
+
+Description:
+
+Tester application uses binary protocol to control Mynewt Nimble stack
+and is aimed at automated testing. It uses Bluetooth Testing Protocol (BTP)
+to drive Bluetooth stack. BTP commands and events are received and buffered for
+further processing.
+--------------------------------------------------------------------------------
+Supported Profiles:
+
+GAP, GATT, SM, L2CAP, MESH
+--------------------------------------------------------------------------------
+
diff --git a/src/libs/mynewt-nimble/apps/bttester/pkg.yml b/src/libs/mynewt-nimble/apps/bttester/pkg.yml
new file mode 100644
index 00000000..00e7a760
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/pkg.yml
@@ -0,0 +1,44 @@
+#
+# 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/bttester
+pkg.type: app
+pkg.description: Bluetooth tester 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"
+ - "@apache-mynewt-core/sys/shell"
+ - "@apache-mynewt-nimble/nimble/controller"
+ - "@apache-mynewt-nimble/nimble/host"
+ - "@apache-mynewt-nimble/nimble/host/util"
+ - "@apache-mynewt-nimble/nimble/host/services/gap"
+ - "@apache-mynewt-nimble/nimble/host/services/gatt"
+ - "@apache-mynewt-nimble/nimble/host/services/dis"
+ - "@apache-mynewt-nimble/nimble/host/store/ram"
+ - "@apache-mynewt-nimble/nimble/transport/ram"
+ - "@apache-mynewt-core/hw/drivers/uart"
+ - "@apache-mynewt-core/hw/drivers/rtt"
+
diff --git a/src/libs/mynewt-nimble/apps/bttester/src/atomic.h b/src/libs/mynewt-nimble/apps/bttester/src/atomic.h
new file mode 100644
index 00000000..66283e9a
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/src/atomic.h
@@ -0,0 +1,405 @@
+/*
+ * 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.
+ */
+
+/* atomic operations */
+
+/*
+ * Copyright (c) 1997-2015, Wind River Systems, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __ATOMIC_H__
+#define __ATOMIC_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef int atomic_t;
+typedef atomic_t atomic_val_t;
+
+/**
+ * @defgroup atomic_apis Atomic Services APIs
+ * @ingroup kernel_apis
+ * @{
+ */
+
+/**
+ * @brief Atomic compare-and-set.
+ *
+ * This routine performs an atomic compare-and-set on @a target. If the current
+ * value of @a target equals @a old_value, @a target is set to @a new_value.
+ * If the current value of @a target does not equal @a old_value, @a target
+ * is left unchanged.
+ *
+ * @param target Address of atomic variable.
+ * @param old_value Original value to compare against.
+ * @param new_value New value to store.
+ * @return 1 if @a new_value is written, 0 otherwise.
+ */
+static inline int atomic_cas(atomic_t *target, atomic_val_t old_value,
+ atomic_val_t new_value)
+{
+ return __atomic_compare_exchange_n(target, &old_value, new_value,
+ 0, __ATOMIC_SEQ_CST,
+ __ATOMIC_SEQ_CST);
+}
+
+/**
+ *
+ * @brief Atomic addition.
+ *
+ * This routine performs an atomic addition on @a target.
+ *
+ * @param target Address of atomic variable.
+ * @param value Value to add.
+ *
+ * @return Previous value of @a target.
+ */
+static inline atomic_val_t atomic_add(atomic_t *target, atomic_val_t value)
+{
+ return __atomic_fetch_add(target, value, __ATOMIC_SEQ_CST);
+}
+
+/**
+ *
+ * @brief Atomic subtraction.
+ *
+ * This routine performs an atomic subtraction on @a target.
+ *
+ * @param target Address of atomic variable.
+ * @param value Value to subtract.
+ *
+ * @return Previous value of @a target.
+ */
+
+static inline atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value)
+{
+ return __atomic_fetch_sub(target, value, __ATOMIC_SEQ_CST);
+}
+
+/**
+ *
+ * @brief Atomic increment.
+ *
+ * This routine performs an atomic increment by 1 on @a target.
+ *
+ * @param target Address of atomic variable.
+ *
+ * @return Previous value of @a target.
+ */
+
+static inline atomic_val_t atomic_inc(atomic_t *target)
+{
+ return atomic_add(target, 1);
+}
+
+/**
+ *
+ * @brief Atomic decrement.
+ *
+ * This routine performs an atomic decrement by 1 on @a target.
+ *
+ * @param target Address of atomic variable.
+ *
+ * @return Previous value of @a target.
+ */
+
+static inline atomic_val_t atomic_dec(atomic_t *target)
+{
+ return atomic_sub(target, 1);
+}
+
+/**
+ *
+ * @brief Atomic get.
+ *
+ * This routine performs an atomic read on @a target.
+ *
+ * @param target Address of atomic variable.
+ *
+ * @return Value of @a target.
+ */
+
+static inline atomic_val_t atomic_get(const atomic_t *target)
+{
+ return __atomic_load_n(target, __ATOMIC_SEQ_CST);
+}
+
+/**
+ *
+ * @brief Atomic get-and-set.
+ *
+ * This routine atomically sets @a target to @a value and returns
+ * the previous value of @a target.
+ *
+ * @param target Address of atomic variable.
+ * @param value Value to write to @a target.
+ *
+ * @return Previous value of @a target.
+ */
+
+static inline atomic_val_t atomic_set(atomic_t *target, atomic_val_t value)
+{
+ /* This builtin, as described by Intel, is not a traditional
+ * test-and-set operation, but rather an atomic exchange operation. It
+ * writes value into *ptr, and returns the previous contents of *ptr.
+ */
+ return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST);
+}
+
+/**
+ *
+ * @brief Atomic clear.
+ *
+ * This routine atomically sets @a target to zero and returns its previous
+ * value. (Hence, it is equivalent to atomic_set(target, 0).)
+ *
+ * @param target Address of atomic variable.
+ *
+ * @return Previous value of @a target.
+ */
+
+static inline atomic_val_t atomic_clear(atomic_t *target)
+{
+ return atomic_set(target, 0);
+}
+
+/**
+ *
+ * @brief Atomic bitwise inclusive OR.
+ *
+ * This routine atomically sets @a target to the bitwise inclusive OR of
+ * @a target and @a value.
+ *
+ * @param target Address of atomic variable.
+ * @param value Value to OR.
+ *
+ * @return Previous value of @a target.
+ */
+
+static inline atomic_val_t atomic_or(atomic_t *target, atomic_val_t value)
+{
+ return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST);
+}
+
+/**
+ *
+ * @brief Atomic bitwise exclusive OR (XOR).
+ *
+ * This routine atomically sets @a target to the bitwise exclusive OR (XOR) of
+ * @a target and @a value.
+ *
+ * @param target Address of atomic variable.
+ * @param value Value to XOR
+ *
+ * @return Previous value of @a target.
+ */
+
+static inline atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value)
+{
+ return __atomic_fetch_xor(target, value, __ATOMIC_SEQ_CST);
+}
+
+/**
+ *
+ * @brief Atomic bitwise AND.
+ *
+ * This routine atomically sets @a target to the bitwise AND of @a target
+ * and @a value.
+ *
+ * @param target Address of atomic variable.
+ * @param value Value to AND.
+ *
+ * @return Previous value of @a target.
+ */
+
+static inline atomic_val_t atomic_and(atomic_t *target, atomic_val_t value)
+{
+ return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST);
+}
+
+/**
+ *
+ * @brief Atomic bitwise NAND.
+ *
+ * This routine atomically sets @a target to the bitwise NAND of @a target
+ * and @a value. (This operation is equivalent to target = ~(target & value).)
+ *
+ * @param target Address of atomic variable.
+ * @param value Value to NAND.
+ *
+ * @return Previous value of @a target.
+ */
+
+static inline atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value)
+{
+ return __atomic_fetch_nand(target, value, __ATOMIC_SEQ_CST);
+}
+
+ /**
+ * @brief Initialize an atomic variable.
+ *
+ * This macro can be used to initialize an atomic variable. For example,
+ * @code atomic_t my_var = ATOMIC_INIT(75); @endcode
+ *
+ * @param i Value to assign to atomic variable.
+ */
+#define ATOMIC_INIT(i) (i)
+
+ /**
+ * @cond INTERNAL_HIDDEN
+ */
+
+#define ATOMIC_BITS (sizeof(atomic_val_t) * 8)
+#define ATOMIC_MASK(bit) (1 << ((bit) & (ATOMIC_BITS - 1)))
+#define ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / ATOMIC_BITS))
+
+ /**
+ * INTERNAL_HIDDEN @endcond
+ */
+
+ /**
+ * @brief Define an array of atomic variables.
+ *
+ * This macro defines an array of atomic variables containing at least
+ * @a num_bits bits.
+ *
+ * @note
+ * If used from file scope, the bits of the array are initialized to zero;
+ * if used from within a function, the bits are left uninitialized.
+ *
+ * @param name Name of array of atomic variables.
+ * @param num_bits Number of bits needed.
+ */
+#define ATOMIC_DEFINE(name, num_bits) \
+ atomic_t name[1 + ((num_bits) - 1) / ATOMIC_BITS]
+
+ /**
+ * @brief Atomically test a bit.
+ *
+ * This routine tests whether bit number @a bit of @a target is set or not.
+ * The target may be a single atomic variable or an array of them.
+ *
+ * @param target Address of atomic variable or array.
+ * @param bit Bit number (starting from 0).
+ *
+ * @return 1 if the bit was set, 0 if it wasn't.
+ */
+ static inline int
+ atomic_test_bit(const atomic_t *target, int bit)
+ {
+ atomic_val_t val = atomic_get(ATOMIC_ELEM(target, bit));
+
+ return (1 & (val >> (bit & (ATOMIC_BITS - 1))));
+ }
+
+ /**
+ * @brief Atomically test and clear a bit.
+ *
+ * Atomically clear bit number @a bit of @a target and return its old value.
+ * The target may be a single atomic variable or an array of them.
+ *
+ * @param target Address of atomic variable or array.
+ * @param bit Bit number (starting from 0).
+ *
+ * @return 1 if the bit was set, 0 if it wasn't.
+ */
+ static inline int
+ atomic_test_and_clear_bit(atomic_t *target, int bit)
+ {
+ atomic_val_t mask = ATOMIC_MASK(bit);
+ atomic_val_t old;
+
+ old = atomic_and(ATOMIC_ELEM(target, bit), ~mask);
+
+ return (old & mask) != 0;
+ }
+
+ /**
+ * @brief Atomically set a bit.
+ *
+ * Atomically set bit number @a bit of @a target and return its old value.
+ * The target may be a single atomic variable or an array of them.
+ *
+ * @param target Address of atomic variable or array.
+ * @param bit Bit number (starting from 0).
+ *
+ * @return 1 if the bit was set, 0 if it wasn't.
+ */
+ static inline int
+ atomic_test_and_set_bit(atomic_t *target, int bit)
+ {
+ atomic_val_t mask = ATOMIC_MASK(bit);
+ atomic_val_t old;
+
+ old = atomic_or(ATOMIC_ELEM(target, bit), mask);
+
+ return (old & mask) != 0;
+ }
+
+ /**
+ * @brief Atomically clear a bit.
+ *
+ * Atomically clear bit number @a bit of @a target.
+ * The target may be a single atomic variable or an array of them.
+ *
+ * @param target Address of atomic variable or array.
+ * @param bit Bit number (starting from 0).
+ *
+ * @return N/A
+ */
+ static inline void
+ atomic_clear_bit(atomic_t *target, int bit)
+ {
+ atomic_val_t mask = ATOMIC_MASK(bit);
+
+ atomic_and(ATOMIC_ELEM(target, bit), ~mask);
+ }
+
+ /**
+ * @brief Atomically set a bit.
+ *
+ * Atomically set bit number @a bit of @a target.
+ * The target may be a single atomic variable or an array of them.
+ *
+ * @param target Address of atomic variable or array.
+ * @param bit Bit number (starting from 0).
+ *
+ * @return N/A
+ */
+ static inline void
+ atomic_set_bit(atomic_t *target, int bit)
+ {
+ atomic_val_t mask = ATOMIC_MASK(bit);
+
+ atomic_or(ATOMIC_ELEM(target, bit), mask);
+ }
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ATOMIC_H__ */
diff --git a/src/libs/mynewt-nimble/apps/bttester/src/bttester.c b/src/libs/mynewt-nimble/apps/bttester/src/bttester.c
new file mode 100644
index 00000000..54b14daa
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/src/bttester.c
@@ -0,0 +1,374 @@
+/*
+ * 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.
+ */
+
+/* bttester.c - Bluetooth Tester */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "syscfg/syscfg.h"
+#include "console/console.h"
+
+#include "bttester_pipe.h"
+#include "bttester.h"
+
+#define CMD_QUEUED 2
+
+static struct os_eventq avail_queue;
+static struct os_eventq *cmds_queue;
+static struct os_event bttester_ev[CMD_QUEUED];
+
+struct btp_buf {
+ struct os_event *ev;
+ union {
+ u8_t data[BTP_MTU];
+ struct btp_hdr hdr;
+ };
+};
+
+static struct btp_buf cmd_buf[CMD_QUEUED];
+
+static void supported_commands(u8_t *data, u16_t len)
+{
+ u8_t buf[1];
+ struct core_read_supported_commands_rp *rp = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ tester_set_bit(buf, CORE_READ_SUPPORTED_COMMANDS);
+ tester_set_bit(buf, CORE_READ_SUPPORTED_SERVICES);
+ tester_set_bit(buf, CORE_REGISTER_SERVICE);
+ tester_set_bit(buf, CORE_UNREGISTER_SERVICE);
+
+ tester_send(BTP_SERVICE_ID_CORE, CORE_READ_SUPPORTED_COMMANDS,
+ BTP_INDEX_NONE, (u8_t *) rp, sizeof(buf));
+}
+
+static void supported_services(u8_t *data, u16_t len)
+{
+ u8_t buf[1];
+ struct core_read_supported_services_rp *rp = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ tester_set_bit(buf, BTP_SERVICE_ID_CORE);
+ tester_set_bit(buf, BTP_SERVICE_ID_GAP);
+ tester_set_bit(buf, BTP_SERVICE_ID_GATT);
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
+ tester_set_bit(buf, BTP_SERVICE_ID_L2CAP);
+#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */
+#if MYNEWT_VAL(BLE_MESH)
+ tester_set_bit(buf, BTP_SERVICE_ID_MESH);
+#endif /* MYNEWT_VAL(BLE_MESH) */
+
+ tester_send(BTP_SERVICE_ID_CORE, CORE_READ_SUPPORTED_SERVICES,
+ BTP_INDEX_NONE, (u8_t *) rp, sizeof(buf));
+}
+
+static void register_service(u8_t *data, u16_t len)
+{
+ struct core_register_service_cmd *cmd = (void *) data;
+ u8_t status;
+
+ switch (cmd->id) {
+ case BTP_SERVICE_ID_GAP:
+ status = tester_init_gap();
+ /* Rsp with success status will be handled by bt enable cb */
+ if (status == BTP_STATUS_FAILED) {
+ goto rsp;
+ }
+ return;
+ case BTP_SERVICE_ID_GATT:
+ status = tester_init_gatt();
+ break;
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
+ case BTP_SERVICE_ID_L2CAP:
+ status = tester_init_l2cap();
+ break;
+#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */
+#if MYNEWT_VAL(BLE_MESH)
+ case BTP_SERVICE_ID_MESH:
+ status = tester_init_mesh();
+ break;
+#endif /* MYNEWT_VAL(BLE_MESH) */
+ default:
+ status = BTP_STATUS_FAILED;
+ break;
+ }
+
+rsp:
+ tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE, BTP_INDEX_NONE,
+ status);
+}
+
+static void unregister_service(u8_t *data, u16_t len)
+{
+ struct core_unregister_service_cmd *cmd = (void *) data;
+ u8_t status;
+
+ switch (cmd->id) {
+ case BTP_SERVICE_ID_GAP:
+ status = tester_unregister_gap();
+ break;
+ case BTP_SERVICE_ID_GATT:
+ status = tester_unregister_gatt();
+ break;
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
+ case BTP_SERVICE_ID_L2CAP:
+ status = tester_unregister_l2cap();
+ break;
+#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */
+#if MYNEWT_VAL(BLE_MESH)
+ case BTP_SERVICE_ID_MESH:
+ status = tester_unregister_mesh();
+ break;
+#endif /* MYNEWT_VAL(BLE_MESH) */
+ default:
+ status = BTP_STATUS_FAILED;
+ break;
+ }
+
+ tester_rsp(BTP_SERVICE_ID_CORE, CORE_UNREGISTER_SERVICE, BTP_INDEX_NONE,
+ status);
+}
+
+static void handle_core(u8_t opcode, u8_t index, u8_t *data,
+ u16_t len)
+{
+ if (index != BTP_INDEX_NONE) {
+ tester_rsp(BTP_SERVICE_ID_CORE, opcode, index,
+ BTP_STATUS_FAILED);
+ return;
+ }
+
+ switch (opcode) {
+ case CORE_READ_SUPPORTED_COMMANDS:
+ supported_commands(data, len);
+ return;
+ case CORE_READ_SUPPORTED_SERVICES:
+ supported_services(data, len);
+ return;
+ case CORE_REGISTER_SERVICE:
+ register_service(data, len);
+ return;
+ case CORE_UNREGISTER_SERVICE:
+ unregister_service(data, len);
+ return;
+ default:
+ tester_rsp(BTP_SERVICE_ID_CORE, opcode, BTP_INDEX_NONE,
+ BTP_STATUS_UNKNOWN_CMD);
+ return;
+ }
+}
+
+static void cmd_handler(struct os_event *ev)
+{
+ u16_t len;
+ struct btp_buf *cmd;
+
+ if (!ev || !ev->ev_arg) {
+ return;
+ }
+
+ cmd = ev->ev_arg;
+
+ len = sys_le16_to_cpu(cmd->hdr.len);
+ if (MYNEWT_VAL(BTTESTER_BTP_LOG)) {
+ console_printf("[DBG] received %d bytes: %s\n",
+ sizeof(cmd->hdr) + len,
+ bt_hex(cmd->data,
+ sizeof(cmd->hdr) + len));
+ }
+
+ /* TODO
+ * verify if service is registered before calling handler
+ */
+
+ switch (cmd->hdr.service) {
+ case BTP_SERVICE_ID_CORE:
+ handle_core(cmd->hdr.opcode, cmd->hdr.index,
+ cmd->hdr.data, len);
+ break;
+ case BTP_SERVICE_ID_GAP:
+ tester_handle_gap(cmd->hdr.opcode, cmd->hdr.index,
+ cmd->hdr.data, len);
+ break;
+ case BTP_SERVICE_ID_GATT:
+ tester_handle_gatt(cmd->hdr.opcode, cmd->hdr.index,
+ cmd->hdr.data, len);
+ break;
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
+ case BTP_SERVICE_ID_L2CAP:
+ tester_handle_l2cap(cmd->hdr.opcode, cmd->hdr.index,
+ cmd->hdr.data, len);
+ break;
+#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */
+#if MYNEWT_VAL(BLE_MESH)
+ case BTP_SERVICE_ID_MESH:
+ tester_handle_mesh(cmd->hdr.opcode, cmd->hdr.index,
+ cmd->hdr.data, len);
+ break;
+#endif /* MYNEWT_VAL(BLE_MESH) */
+ default:
+ tester_rsp(cmd->hdr.service, cmd->hdr.opcode,
+ cmd->hdr.index, BTP_STATUS_FAILED);
+ break;
+ }
+
+ os_eventq_put(&avail_queue, ev);
+}
+
+static u8_t *recv_cb(u8_t *buf, size_t *off)
+{
+ struct btp_hdr *cmd = (void *) buf;
+ struct os_event *new_ev;
+ struct btp_buf *new_buf, *old_buf;
+ u16_t len;
+
+ if (*off < sizeof(*cmd)) {
+ return buf;
+ }
+
+ len = sys_le16_to_cpu(cmd->len);
+ if (len > BTP_MTU - sizeof(*cmd)) {
+ *off = 0;
+ return buf;
+ }
+
+ if (*off < sizeof(*cmd) + len) {
+ return buf;
+ }
+
+ new_ev = os_eventq_get_no_wait(&avail_queue);
+ if (!new_ev) {
+ SYS_LOG_ERR("BT tester: RX overflow");
+ *off = 0;
+ return buf;
+ }
+
+ old_buf = CONTAINER_OF(buf, struct btp_buf, data);
+ os_eventq_put(cmds_queue, old_buf->ev);
+
+ new_buf = new_ev->ev_arg;
+ *off = 0;
+ return new_buf->data;
+}
+
+static void avail_queue_init(void)
+{
+ int i;
+
+ os_eventq_init(&avail_queue);
+
+ for (i = 0; i < CMD_QUEUED; i++) {
+ cmd_buf[i].ev = &bttester_ev[i];
+ bttester_ev[i].ev_cb = cmd_handler;
+ bttester_ev[i].ev_arg = &cmd_buf[i];
+
+ os_eventq_put(&avail_queue, &bttester_ev[i]);
+ }
+}
+
+void bttester_evq_set(struct os_eventq *evq)
+{
+ cmds_queue = evq;
+}
+
+void tester_init(void)
+{
+ struct os_event *ev;
+ struct btp_buf *buf;
+
+ avail_queue_init();
+ bttester_evq_set(os_eventq_dflt_get());
+
+ ev = os_eventq_get(&avail_queue);
+ buf = ev->ev_arg;
+
+ if (bttester_pipe_init()) {
+ SYS_LOG_ERR("Failed to initialize pipe");
+ return;
+ }
+
+ bttester_pipe_register(buf->data, BTP_MTU, recv_cb);
+
+ tester_send(BTP_SERVICE_ID_CORE, CORE_EV_IUT_READY, BTP_INDEX_NONE,
+ NULL, 0);
+}
+
+void tester_send(u8_t service, u8_t opcode, u8_t index, u8_t *data,
+ size_t len)
+{
+ struct btp_hdr msg;
+
+ msg.service = service;
+ msg.opcode = opcode;
+ msg.index = index;
+ msg.len = len;
+
+ bttester_pipe_send((u8_t *)&msg, sizeof(msg));
+ if (data && len) {
+ bttester_pipe_send(data, len);
+ }
+
+ if (MYNEWT_VAL(BTTESTER_BTP_LOG)) {
+ console_printf("[DBG] send %d bytes hdr: %s\n", sizeof(msg),
+ bt_hex((char *) &msg, sizeof(msg)));
+ if (data && len) {
+ console_printf("[DBG] send %d bytes data: %s\n", len,
+ bt_hex((char *) data, len));
+ }
+ }
+}
+
+void tester_send_buf(u8_t service, u8_t opcode, u8_t index,
+ struct os_mbuf *data)
+{
+ struct btp_hdr msg;
+
+ msg.service = service;
+ msg.opcode = opcode;
+ msg.index = index;
+ msg.len = os_mbuf_len(data);
+
+ bttester_pipe_send((u8_t *)&msg, sizeof(msg));
+ if (data && msg.len) {
+ bttester_pipe_send_buf(data);
+ }
+}
+
+void tester_rsp(u8_t service, u8_t opcode, u8_t index, u8_t status)
+{
+ struct btp_status s;
+
+ if (status == BTP_STATUS_SUCCESS) {
+ tester_send(service, opcode, index, NULL, 0);
+ return;
+ }
+
+ s.code = status;
+ tester_send(service, BTP_STATUS, index, (u8_t *) &s, sizeof(s));
+}
diff --git a/src/libs/mynewt-nimble/apps/bttester/src/bttester.h b/src/libs/mynewt-nimble/apps/bttester/src/bttester.h
new file mode 100644
index 00000000..f4e66a6f
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/src/bttester.h
@@ -0,0 +1,1010 @@
+/*
+ * 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.
+ */
+
+/* bttester.h - Bluetooth tester headers */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __BTTESTER_H__
+#define __BTTESTER_H__
+
+#include "syscfg/syscfg.h"
+#include "host/ble_gatt.h"
+
+#if MYNEWT_VAL(BLE_MESH)
+#include "mesh/glue.h"
+#else
+#include "glue.h"
+#endif
+
+#define BTP_MTU MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX)
+#define BTP_DATA_MAX_SIZE (BTP_MTU - sizeof(struct btp_hdr))
+
+#define BTP_INDEX_NONE 0xff
+
+#define BTP_SERVICE_ID_CORE 0
+#define BTP_SERVICE_ID_GAP 1
+#define BTP_SERVICE_ID_GATT 2
+#define BTP_SERVICE_ID_L2CAP 3
+#define BTP_SERVICE_ID_MESH 4
+
+#define BTP_STATUS_SUCCESS 0x00
+#define BTP_STATUS_FAILED 0x01
+#define BTP_STATUS_UNKNOWN_CMD 0x02
+#define BTP_STATUS_NOT_READY 0x03
+
+#define SYS_LOG_DBG(fmt, ...) \
+ if (MYNEWT_VAL(BTTESTER_DEBUG)) { \
+ console_printf("[DBG] %s: " fmt "\n", \
+ __func__, ## __VA_ARGS__); \
+ }
+#define SYS_LOG_INF(fmt, ...) console_printf("[INF] %s: " fmt "\n", \
+ __func__, ## __VA_ARGS__);
+#define SYS_LOG_ERR(fmt, ...) console_printf("[WRN] %s: " fmt "\n", \
+ __func__, ## __VA_ARGS__);
+
+#define SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
+#define SYS_LOG_DOMAIN "bttester"
+
+#define sys_cpu_to_le32 htole32
+#define sys_le32_to_cpu le32toh
+#define sys_cpu_to_le16 htole16
+
+struct btp_hdr {
+ u8_t service;
+ u8_t opcode;
+ u8_t index;
+ u16_t len;
+ u8_t data[0];
+} __packed;
+
+#define BTP_STATUS 0x00
+struct btp_status {
+ u8_t code;
+} __packed;
+
+/* Core Service */
+#define CORE_READ_SUPPORTED_COMMANDS 0x01
+struct core_read_supported_commands_rp {
+ u8_t data[0];
+} __packed;
+
+#define CORE_READ_SUPPORTED_SERVICES 0x02
+struct core_read_supported_services_rp {
+ u8_t data[0];
+} __packed;
+
+#define CORE_REGISTER_SERVICE 0x03
+struct core_register_service_cmd {
+ u8_t id;
+} __packed;
+
+#define CORE_UNREGISTER_SERVICE 0x04
+struct core_unregister_service_cmd {
+ u8_t id;
+} __packed;
+
+/* events */
+#define CORE_EV_IUT_READY 0x80
+
+/* GAP Service */
+/* commands */
+#define GAP_READ_SUPPORTED_COMMANDS 0x01
+struct gap_read_supported_commands_rp {
+ u8_t data[0];
+} __packed;
+
+#define GAP_READ_CONTROLLER_INDEX_LIST 0x02
+struct gap_read_controller_index_list_rp {
+ u8_t num;
+ u8_t index[0];
+} __packed;
+
+#define GAP_SETTINGS_POWERED 0
+#define GAP_SETTINGS_CONNECTABLE 1
+#define GAP_SETTINGS_FAST_CONNECTABLE 2
+#define GAP_SETTINGS_DISCOVERABLE 3
+#define GAP_SETTINGS_BONDABLE 4
+#define GAP_SETTINGS_LINK_SEC_3 5
+#define GAP_SETTINGS_SSP 6
+#define GAP_SETTINGS_BREDR 7
+#define GAP_SETTINGS_HS 8
+#define GAP_SETTINGS_LE 9
+#define GAP_SETTINGS_ADVERTISING 10
+#define GAP_SETTINGS_SC 11
+#define GAP_SETTINGS_DEBUG_KEYS 12
+#define GAP_SETTINGS_PRIVACY 13
+#define GAP_SETTINGS_CONTROLLER_CONFIG 14
+#define GAP_SETTINGS_STATIC_ADDRESS 15
+
+#define GAP_READ_CONTROLLER_INFO 0x03
+struct gap_read_controller_info_rp {
+ u8_t address[6];
+ u32_t supported_settings;
+ u32_t current_settings;
+ u8_t cod[3];
+ u8_t name[249];
+ u8_t short_name[11];
+} __packed;
+
+#define GAP_RESET 0x04
+struct gap_reset_rp {
+ u32_t current_settings;
+} __packed;
+
+#define GAP_SET_POWERED 0x05
+struct gap_set_powered_cmd {
+ u8_t powered;
+} __packed;
+struct gap_set_powered_rp {
+ u32_t current_settings;
+} __packed;
+
+#define GAP_SET_CONNECTABLE 0x06
+struct gap_set_connectable_cmd {
+ u8_t connectable;
+} __packed;
+struct gap_set_connectable_rp {
+ u32_t current_settings;
+} __packed;
+
+#define GAP_SET_FAST_CONNECTABLE 0x07
+struct gap_set_fast_connectable_cmd {
+ u8_t fast_connectable;
+} __packed;
+struct gap_set_fast_connectable_rp {
+ u32_t current_settings;
+} __packed;
+
+#define GAP_NON_DISCOVERABLE 0x00
+#define GAP_GENERAL_DISCOVERABLE 0x01
+#define GAP_LIMITED_DISCOVERABLE 0x02
+
+#define GAP_SET_DISCOVERABLE 0x08
+struct gap_set_discoverable_cmd {
+ u8_t discoverable;
+} __packed;
+struct gap_set_discoverable_rp {
+ u32_t current_settings;
+} __packed;
+
+#define GAP_SET_BONDABLE 0x09
+struct gap_set_bondable_cmd {
+ u8_t bondable;
+} __packed;
+struct gap_set_bondable_rp {
+ u32_t current_settings;
+} __packed;
+
+#define GAP_START_ADVERTISING 0x0a
+struct gap_start_advertising_cmd {
+ u8_t adv_data_len;
+ u8_t scan_rsp_len;
+ u8_t adv_data[0];
+ u8_t scan_rsp[0];
+} __packed;
+struct gap_start_advertising_rp {
+ u32_t current_settings;
+} __packed;
+
+#define GAP_STOP_ADVERTISING 0x0b
+struct gap_stop_advertising_rp {
+ u32_t current_settings;
+} __packed;
+
+#define GAP_DISCOVERY_FLAG_LE 0x01
+#define GAP_DISCOVERY_FLAG_BREDR 0x02
+#define GAP_DISCOVERY_FLAG_LIMITED 0x04
+#define GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN 0x08
+#define GAP_DISCOVERY_FLAG_LE_OBSERVE 0x10
+
+#define GAP_START_DISCOVERY 0x0c
+struct gap_start_discovery_cmd {
+ u8_t flags;
+} __packed;
+
+#define GAP_STOP_DISCOVERY 0x0d
+
+#define GAP_CONNECT 0x0e
+struct gap_connect_cmd {
+ u8_t address_type;
+ u8_t address[6];
+} __packed;
+
+#define GAP_DISCONNECT 0x0f
+struct gap_disconnect_cmd {
+ u8_t address_type;
+ u8_t address[6];
+} __packed;
+
+#define GAP_IO_CAP_DISPLAY_ONLY 0
+#define GAP_IO_CAP_DISPLAY_YESNO 1
+#define GAP_IO_CAP_KEYBOARD_ONLY 2
+#define GAP_IO_CAP_NO_INPUT_OUTPUT 3
+#define GAP_IO_CAP_KEYBOARD_DISPLAY 4
+
+#define GAP_SET_IO_CAP 0x10
+struct gap_set_io_cap_cmd {
+ u8_t io_cap;
+} __packed;
+
+#define GAP_PAIR 0x11
+struct gap_pair_cmd {
+ u8_t address_type;
+ u8_t address[6];
+} __packed;
+
+#define GAP_UNPAIR 0x12
+struct gap_unpair_cmd {
+ u8_t address_type;
+ u8_t address[6];
+} __packed;
+
+#define GAP_PASSKEY_ENTRY 0x13
+struct gap_passkey_entry_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u32_t passkey;
+} __packed;
+
+#define GAP_PASSKEY_CONFIRM 0x14
+struct gap_passkey_confirm_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u8_t match;
+} __packed;
+
+#define GAP_START_DIRECT_ADV 0x15
+struct gap_start_direct_adv_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u8_t high_duty;
+} __packed;
+
+#define GAP_CONN_PARAM_UPDATE 0x16
+struct gap_conn_param_update_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t conn_itvl_min;
+ u16_t conn_itvl_max;
+ u16_t conn_latency;
+ u16_t supervision_timeout;
+} __packed;
+
+#define GAP_PAIRING_CONSENT_RSP 0x17
+struct gap_pairing_consent_rsp_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u8_t consent;
+} __packed;
+
+#define GAP_OOB_LEGACY_SET_DATA 0x18
+struct gap_oob_legacy_set_data_cmd {
+ u8_t oob_data[16];
+} __packed;
+
+#define GAP_OOB_SC_GET_LOCAL_DATA 0x19
+struct gap_oob_sc_get_local_data_rp {
+ u8_t r[16];
+ u8_t c[16];
+} __packed;
+
+#define GAP_OOB_SC_SET_REMOTE_DATA 0x1a
+struct gap_oob_sc_set_remote_data_cmd {
+ u8_t r[16];
+ u8_t c[16];
+} __packed;
+
+#define GAP_SET_MITM 0x1b
+struct gap_set_mitm_cmd {
+ u8_t mitm;
+} __packed;
+
+/* events */
+#define GAP_EV_NEW_SETTINGS 0x80
+struct gap_new_settings_ev {
+ u32_t current_settings;
+} __packed;
+
+#define GAP_DEVICE_FOUND_FLAG_RSSI 0x01
+#define GAP_DEVICE_FOUND_FLAG_AD 0x02
+#define GAP_DEVICE_FOUND_FLAG_SD 0x04
+
+#define GAP_EV_DEVICE_FOUND 0x81
+struct gap_device_found_ev {
+ u8_t address_type;
+ u8_t address[6];
+ s8_t rssi;
+ u8_t flags;
+ u16_t eir_data_len;
+ u8_t eir_data[0];
+} __packed;
+
+#define GAP_EV_DEVICE_CONNECTED 0x82
+struct gap_device_connected_ev {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t conn_itvl;
+ u16_t conn_latency;
+ u16_t supervision_timeout;
+} __packed;
+
+#define GAP_EV_DEVICE_DISCONNECTED 0x83
+struct gap_device_disconnected_ev {
+ u8_t address_type;
+ u8_t address[6];
+} __packed;
+
+#define GAP_EV_PASSKEY_DISPLAY 0x84
+struct gap_passkey_display_ev {
+ u8_t address_type;
+ u8_t address[6];
+ u32_t passkey;
+} __packed;
+
+#define GAP_EV_PASSKEY_ENTRY_REQ 0x85
+struct gap_passkey_entry_req_ev {
+ u8_t address_type;
+ u8_t address[6];
+} __packed;
+
+#define GAP_EV_PASSKEY_CONFIRM_REQ 0x86
+struct gap_passkey_confirm_req_ev {
+ u8_t address_type;
+ u8_t address[6];
+ u32_t passkey;
+} __packed;
+
+#define GAP_EV_IDENTITY_RESOLVED 0x87
+struct gap_identity_resolved_ev {
+ u8_t address_type;
+ u8_t address[6];
+ u8_t identity_address_type;
+ u8_t identity_address[6];
+} __packed;
+
+#define GAP_EV_CONN_PARAM_UPDATE 0x88
+struct gap_conn_param_update_ev {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t conn_itvl;
+ u16_t conn_latency;
+ u16_t supervision_timeout;
+} __packed;
+
+#define GAP_EV_SEC_LEVEL_CHANGED 0x89
+struct gap_sec_level_changed_ev {
+ u8_t address_type;
+ u8_t address[6];
+ u8_t level;
+} __packed;
+
+#define GAP_EV_PAIRING_CONSENT_REQ 0x8a
+struct gap_pairing_consent_req_ev {
+ u8_t address_type;
+ u8_t address[6];
+} __packed;
+
+/* GATT Service */
+/* commands */
+#define GATT_READ_SUPPORTED_COMMANDS 0x01
+struct gatt_read_supported_commands_rp {
+ u8_t data[0];
+} __packed;
+
+#define GATT_SERVICE_PRIMARY 0x00
+#define GATT_SERVICE_SECONDARY 0x01
+
+#define GATT_ADD_SERVICE 0x02
+struct gatt_add_service_cmd {
+ u8_t type;
+ u8_t uuid_length;
+ u8_t uuid[0];
+} __packed;
+struct gatt_add_service_rp {
+ u16_t svc_id;
+} __packed;
+
+#define GATT_ADD_CHARACTERISTIC 0x03
+struct gatt_add_characteristic_cmd {
+ u16_t svc_id;
+ u8_t properties;
+ u8_t permissions;
+ u8_t uuid_length;
+ u8_t uuid[0];
+} __packed;
+struct gatt_add_characteristic_rp {
+ u16_t char_id;
+} __packed;
+
+#define GATT_ADD_DESCRIPTOR 0x04
+struct gatt_add_descriptor_cmd {
+ u16_t char_id;
+ u8_t permissions;
+ u8_t uuid_length;
+ u8_t uuid[0];
+} __packed;
+struct gatt_add_descriptor_rp {
+ u16_t desc_id;
+} __packed;
+
+#define GATT_ADD_INCLUDED_SERVICE 0x05
+struct gatt_add_included_service_cmd {
+ u16_t svc_id;
+} __packed;
+struct gatt_add_included_service_rp {
+ u16_t included_service_id;
+} __packed;
+
+#define GATT_SET_VALUE 0x06
+ struct gatt_set_value_cmd {
+ u16_t attr_id;
+ u16_t len;
+ u8_t value[0];
+} __packed;
+
+#define GATT_START_SERVER 0x07
+struct gatt_start_server_rp {
+ u16_t db_attr_off;
+ u8_t db_attr_cnt;
+} __packed;
+
+#define GATT_SET_ENC_KEY_SIZE 0x09
+struct gatt_set_enc_key_size_cmd {
+ u16_t attr_id;
+ u8_t key_size;
+} __packed;
+
+/* Gatt Client */
+struct gatt_service {
+ u16_t start_handle;
+ u16_t end_handle;
+ u8_t uuid_length;
+ u8_t uuid[0];
+} __packed;
+
+struct gatt_included {
+ u16_t included_handle;
+ struct gatt_service service;
+} __packed;
+
+struct gatt_characteristic {
+ u16_t characteristic_handle;
+ u16_t value_handle;
+ u8_t properties;
+ u8_t uuid_length;
+ u8_t uuid[0];
+} __packed;
+
+struct gatt_descriptor {
+ u16_t descriptor_handle;
+ u8_t uuid_length;
+ u8_t uuid[0];
+} __packed;
+
+#define GATT_EXCHANGE_MTU 0x0a
+
+#define GATT_DISC_ALL_PRIM_SVCS 0x0b
+struct gatt_disc_all_prim_svcs_cmd {
+ u8_t address_type;
+ u8_t address[6];
+} __packed;
+struct gatt_disc_all_prim_svcs_rp {
+ u8_t services_count;
+ struct gatt_service services[0];
+} __packed;
+
+#define GATT_DISC_PRIM_UUID 0x0c
+struct gatt_disc_prim_uuid_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u8_t uuid_length;
+ u8_t uuid[0];
+} __packed;
+struct gatt_disc_prim_uuid_rp {
+ u8_t services_count;
+ struct gatt_service services[0];
+} __packed;
+
+#define GATT_FIND_INCLUDED 0x0d
+struct gatt_find_included_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t start_handle;
+ u16_t end_handle;
+} __packed;
+struct gatt_find_included_rp {
+ u8_t services_count;
+ struct gatt_included included[0];
+} __packed;
+
+#define GATT_DISC_ALL_CHRC 0x0e
+struct gatt_disc_all_chrc_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t start_handle;
+ u16_t end_handle;
+} __packed;
+struct gatt_disc_chrc_rp {
+ u8_t characteristics_count;
+ struct gatt_characteristic characteristics[0];
+} __packed;
+
+#define GATT_DISC_CHRC_UUID 0x0f
+struct gatt_disc_chrc_uuid_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t start_handle;
+ u16_t end_handle;
+ u8_t uuid_length;
+ u8_t uuid[0];
+} __packed;
+
+#define GATT_DISC_ALL_DESC 0x10
+struct gatt_disc_all_desc_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t start_handle;
+ u16_t end_handle;
+} __packed;
+struct gatt_disc_all_desc_rp {
+ u8_t descriptors_count;
+ struct gatt_descriptor descriptors[0];
+} __packed;
+
+#define GATT_READ 0x11
+struct gatt_read_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t handle;
+} __packed;
+struct gatt_read_rp {
+ u8_t att_response;
+ u16_t data_length;
+ u8_t data[0];
+} __packed;
+
+#define GATT_READ_UUID 0x12
+struct gatt_read_uuid_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t start_handle;
+ u16_t end_handle;
+ u8_t uuid_length;
+ u8_t uuid[0];
+} __packed;
+
+#define GATT_READ_LONG 0x13
+struct gatt_read_long_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t handle;
+ u16_t offset;
+} __packed;
+
+#define GATT_READ_MULTIPLE 0x14
+struct gatt_read_multiple_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u8_t handles_count;
+ u16_t handles[0];
+} __packed;
+
+#define GATT_WRITE_WITHOUT_RSP 0x15
+struct gatt_write_without_rsp_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t handle;
+ u16_t data_length;
+ u8_t data[0];
+} __packed;
+
+#define GATT_SIGNED_WRITE_WITHOUT_RSP 0x16
+struct gatt_signed_write_without_rsp_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t handle;
+ u16_t data_length;
+ u8_t data[0];
+} __packed;
+
+#define GATT_WRITE 0x17
+struct gatt_write_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t handle;
+ u16_t data_length;
+ u8_t data[0];
+} __packed;
+
+#define GATT_WRITE_LONG 0x18
+struct gatt_write_long_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t handle;
+ u16_t offset;
+ u16_t data_length;
+ u8_t data[0];
+} __packed;
+
+#define GATT_RELIABLE_WRITE 0x19
+struct gatt_reliable_write_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t handle;
+ u16_t offset;
+ u16_t data_length;
+ u8_t data[0];
+} __packed;
+
+#define GATT_CFG_NOTIFY 0x1a
+#define GATT_CFG_INDICATE 0x1b
+struct gatt_cfg_notify_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u8_t enable;
+ u16_t ccc_handle;
+} __packed;
+
+#define GATT_GET_ATTRIBUTES 0x1c
+struct gatt_get_attributes_cmd {
+ u16_t start_handle;
+ u16_t end_handle;
+ u8_t type_length;
+ u8_t type[0];
+} __packed;
+struct gatt_get_attributes_rp {
+ u8_t attrs_count;
+ u8_t attrs[0];
+} __packed;
+struct gatt_attr {
+ u16_t handle;
+ u8_t permission;
+ u8_t type_length;
+ u8_t type[0];
+} __packed;
+
+#define GATT_GET_ATTRIBUTE_VALUE 0x1d
+struct gatt_get_attribute_value_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t handle;
+} __packed;
+struct gatt_get_attribute_value_rp {
+ u8_t att_response;
+ u16_t value_length;
+ u8_t value[0];
+} __packed;
+
+#define GATT_CHANGE_DATABASE 0x1e
+struct gatt_change_database {
+ u16_t start_handle;
+ u16_t end_handle;
+ u8_t visibility;
+} __packed;
+
+/* GATT events */
+#define GATT_EV_NOTIFICATION 0x80
+struct gatt_notification_ev {
+ u8_t address_type;
+ u8_t address[6];
+ u8_t type;
+ u16_t handle;
+ u16_t data_length;
+ u8_t data[0];
+} __packed;
+
+#define GATT_EV_ATTR_VALUE_CHANGED 0x81
+struct gatt_attr_value_changed_ev {
+ u16_t handle;
+ u16_t data_length;
+ u8_t data[0];
+} __packed;
+
+static inline void tester_set_bit(u8_t *addr, unsigned int bit)
+{
+ u8_t *p = addr + (bit / 8);
+
+ *p |= BIT(bit % 8);
+}
+
+static inline u8_t tester_test_bit(const u8_t *addr, unsigned int bit)
+{
+ const u8_t *p = addr + (bit / 8);
+
+ return *p & BIT(bit % 8);
+}
+
+/* L2CAP Service */
+/* commands */
+#define L2CAP_READ_SUPPORTED_COMMANDS 0x01
+struct l2cap_read_supported_commands_rp {
+ u8_t data[0];
+} __packed;
+
+#define L2CAP_CONNECT 0x02
+struct l2cap_connect_cmd {
+ u8_t address_type;
+ u8_t address[6];
+ u16_t psm;
+} __packed;
+
+struct l2cap_connect_rp {
+ u8_t chan_id;
+} __packed;
+
+#define L2CAP_DISCONNECT 0x03
+struct l2cap_disconnect_cmd {
+ u8_t chan_id;
+} __packed;
+
+#define L2CAP_SEND_DATA 0x04
+struct l2cap_send_data_cmd {
+ u8_t chan_id;
+ u16_t data_len;
+ u8_t data[];
+} __packed;
+
+#define L2CAP_TRANSPORT_BREDR 0x00
+#define L2CAP_TRANSPORT_LE 0x01
+
+#define L2CAP_LISTEN 0x05
+struct l2cap_listen_cmd {
+ u16_t psm;
+ u8_t transport;
+} __packed;
+
+#define L2CAP_ACCEPT_CONNECTION 0x06
+struct l2cap_accept_connection_cmd {
+ u8_t chan_id;
+ u16_t result;
+} __packed;
+
+/* events */
+#define L2CAP_EV_CONNECTION_REQ 0x80
+struct l2cap_connection_req_ev {
+ u8_t chan_id;
+ u16_t psm;
+ u8_t address_type;
+ u8_t address[6];
+} __packed;
+
+#define L2CAP_EV_CONNECTED 0x81
+struct l2cap_connected_ev {
+ u8_t chan_id;
+ u16_t psm;
+ u8_t address_type;
+ u8_t address[6];
+} __packed;
+
+#define L2CAP_EV_DISCONNECTED 0x82
+struct l2cap_disconnected_ev {
+ u16_t result;
+ u8_t chan_id;
+ u16_t psm;
+ u8_t address_type;
+ u8_t address[6];
+} __packed;
+
+#define L2CAP_EV_DATA_RECEIVED 0x83
+struct l2cap_data_received_ev {
+ u8_t chan_id;
+ u16_t data_length;
+ u8_t data[0];
+} __packed;
+
+/* MESH Service */
+/* commands */
+#define MESH_READ_SUPPORTED_COMMANDS 0x01
+struct mesh_read_supported_commands_rp {
+ u8_t data[0];
+} __packed;
+
+#define MESH_OUT_BLINK BIT(0)
+#define MESH_OUT_BEEP BIT(1)
+#define MESH_OUT_VIBRATE BIT(2)
+#define MESH_OUT_DISPLAY_NUMBER BIT(3)
+#define MESH_OUT_DISPLAY_STRING BIT(4)
+
+#define MESH_IN_PUSH BIT(0)
+#define MESH_IN_TWIST BIT(1)
+#define MESH_IN_ENTER_NUMBER BIT(2)
+#define MESH_IN_ENTER_STRING BIT(3)
+
+#define MESH_CONFIG_PROVISIONING 0x02
+struct mesh_config_provisioning_cmd {
+ u8_t uuid[16];
+ u8_t static_auth[16];
+ u8_t out_size;
+ u16_t out_actions;
+ u8_t in_size;
+ u16_t in_actions;
+} __packed;
+
+#define MESH_PROVISION_NODE 0x03
+struct mesh_provision_node_cmd {
+ u8_t net_key[16];
+ u16_t net_key_idx;
+ u8_t flags;
+ u32_t iv_index;
+ u32_t seq_num;
+ u16_t addr;
+ u8_t dev_key[16];
+} __packed;
+
+#define MESH_INIT 0x04
+#define MESH_RESET 0x05
+#define MESH_INPUT_NUMBER 0x06
+struct mesh_input_number_cmd {
+ u32_t number;
+} __packed;
+
+#define MESH_INPUT_STRING 0x07
+struct mesh_input_string_cmd {
+ u8_t string_len;
+ u8_t string[0];
+} __packed;
+
+#define MESH_IVU_TEST_MODE 0x08
+struct mesh_ivu_test_mode_cmd {
+ u8_t enable;
+} __packed;
+
+#define MESH_IVU_TOGGLE_STATE 0x09
+
+#define MESH_NET_SEND 0x0a
+struct mesh_net_send_cmd {
+ u8_t ttl;
+ u16_t src;
+ u16_t dst;
+ u8_t payload_len;
+ u8_t payload[0];
+} __packed;
+
+#define MESH_HEALTH_GENERATE_FAULTS 0x0b
+struct mesh_health_generate_faults_rp {
+ u8_t test_id;
+ u8_t cur_faults_count;
+ u8_t reg_faults_count;
+ u8_t current_faults[0];
+ u8_t registered_faults[0];
+} __packed;
+
+#define MESH_HEALTH_CLEAR_FAULTS 0x0c
+
+#define MESH_LPN 0x0d
+struct mesh_lpn_set_cmd {
+ u8_t enable;
+} __packed;
+
+#define MESH_LPN_POLL 0x0e
+
+#define MESH_MODEL_SEND 0x0f
+struct mesh_model_send_cmd {
+ u16_t src;
+ u16_t dst;
+ u8_t payload_len;
+ u8_t payload[0];
+} __packed;
+
+#define MESH_LPN_SUBSCRIBE 0x10
+struct mesh_lpn_subscribe_cmd {
+ u16_t address;
+} __packed;
+
+#define MESH_LPN_UNSUBSCRIBE 0x11
+struct mesh_lpn_unsubscribe_cmd {
+ u16_t address;
+} __packed;
+
+#define MESH_RPL_CLEAR 0x12
+#define MESH_PROXY_IDENTITY 0x13
+
+/* events */
+#define MESH_EV_OUT_NUMBER_ACTION 0x80
+struct mesh_out_number_action_ev {
+ u16_t action;
+ u32_t number;
+} __packed;
+
+#define MESH_EV_OUT_STRING_ACTION 0x81
+struct mesh_out_string_action_ev {
+ u8_t string_len;
+ u8_t string[0];
+} __packed;
+
+#define MESH_EV_IN_ACTION 0x82
+struct mesh_in_action_ev {
+ u16_t action;
+ u8_t size;
+} __packed;
+
+#define MESH_EV_PROVISIONED 0x83
+
+#define MESH_PROV_BEARER_PB_ADV 0x00
+#define MESH_PROV_BEARER_PB_GATT 0x01
+#define MESH_EV_PROV_LINK_OPEN 0x84
+struct mesh_prov_link_open_ev {
+ u8_t bearer;
+} __packed;
+
+#define MESH_EV_PROV_LINK_CLOSED 0x85
+struct mesh_prov_link_closed_ev {
+ u8_t bearer;
+} __packed;
+
+#define MESH_EV_NET_RECV 0x86
+struct mesh_net_recv_ev {
+ u8_t ttl;
+ u8_t ctl;
+ u16_t src;
+ u16_t dst;
+ u8_t payload_len;
+ u8_t payload[0];
+} __packed;
+
+#define MESH_EV_INVALID_BEARER 0x87
+struct mesh_invalid_bearer_ev {
+ u8_t opcode;
+} __packed;
+
+#define MESH_EV_INCOMP_TIMER_EXP 0x88
+
+void tester_init(void);
+void tester_rsp(u8_t service, u8_t opcode, u8_t index, u8_t status);
+void tester_send(u8_t service, u8_t opcode, u8_t index, u8_t *data,
+ size_t len);
+void tester_send_buf(u8_t service, u8_t opcode, u8_t index,
+ struct os_mbuf *buf);
+
+u8_t tester_init_gap(void);
+u8_t tester_unregister_gap(void);
+void tester_handle_gap(u8_t opcode, u8_t index, u8_t *data,
+ u16_t len);
+u8_t tester_init_gatt(void);
+u8_t tester_unregister_gatt(void);
+void tester_handle_gatt(u8_t opcode, u8_t index, u8_t *data,
+ u16_t len);
+int tester_gatt_notify_rx_ev(u16_t conn_handle, u16_t attr_handle,
+ u8_t indication, struct os_mbuf *om);
+int tester_gatt_subscribe_ev(u16_t conn_handle, u16_t attr_handle, u8_t reason,
+ u8_t prev_notify, u8_t cur_notify,
+ u8_t prev_indicate, u8_t cur_indicate);
+
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
+u8_t tester_init_l2cap(void);
+u8_t tester_unregister_l2cap(void);
+void tester_handle_l2cap(u8_t opcode, u8_t index, u8_t *data,
+ u16_t len);
+#endif
+
+#if MYNEWT_VAL(BLE_MESH)
+u8_t tester_init_mesh(void);
+u8_t tester_unregister_mesh(void);
+void tester_handle_mesh(u8_t opcode, u8_t index, u8_t *data, u16_t len);
+#endif /* MYNEWT_VAL(BLE_MESH) */
+
+void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
+int gatt_svr_init(void);
+
+#endif /* __BTTESTER_H__ */
diff --git a/src/libs/mynewt-nimble/apps/bttester/src/bttester_pipe.h b/src/libs/mynewt-nimble/apps/bttester/src/bttester_pipe.h
new file mode 100644
index 00000000..c54d42de
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/src/bttester_pipe.h
@@ -0,0 +1,40 @@
+/*
+ * 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 __BTTESTER_PIPE_H__
+#define __BTTESTER_PIPE_H__
+
+#include <stdlib.h>
+#include "bttester.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef u8_t *(*bttester_pipe_recv_cb)(u8_t *buf, size_t *off);
+void bttester_pipe_register(u8_t *buf, size_t len, bttester_pipe_recv_cb cb);
+int bttester_pipe_send(const u8_t *data, int len);
+int bttester_pipe_send_buf(struct os_mbuf *buf);
+int bttester_pipe_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BTTESTER_PIPE_H__ */
diff --git a/src/libs/mynewt-nimble/apps/bttester/src/gap.c b/src/libs/mynewt-nimble/apps/bttester/src/gap.c
new file mode 100644
index 00000000..9d6de043
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/src/gap.c
@@ -0,0 +1,1688 @@
+/*
+ * 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.
+ */
+
+/* gap.c - Bluetooth GAP Tester */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "host/ble_gap.h"
+#include "host/util/util.h"
+#include "console/console.h"
+
+#include "../../../nimble/host/src/ble_hs_pvcy_priv.h"
+#include "../../../nimble/host/src/ble_hs_hci_priv.h"
+#include "../../../nimble/host/src/ble_sm_priv.h"
+
+#include "bttester.h"
+
+#define CONTROLLER_INDEX 0
+#define CONTROLLER_NAME "btp_tester"
+
+#define BLE_AD_DISCOV_MASK (BLE_HS_ADV_F_DISC_LTD | BLE_HS_ADV_F_DISC_GEN)
+#define ADV_BUF_LEN (sizeof(struct gap_device_found_ev) + 2 * 31)
+
+const uint8_t irk[16] = {
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+};
+
+static uint8_t oob[16];
+static struct ble_sm_sc_oob_data oob_data_local;
+static struct ble_sm_sc_oob_data oob_data_remote;
+
+static uint16_t current_settings;
+u8_t own_addr_type;
+static ble_addr_t peer_id_addr;
+static ble_addr_t peer_ota_addr;
+static bool encrypted = false;
+
+static struct os_callout update_params_co;
+static struct gap_conn_param_update_cmd update_params;
+
+static struct os_callout connected_ev_co;
+static struct gap_device_connected_ev connected_ev;
+#define CONNECTED_EV_DELAY_MS(itvl) 8 * BLE_HCI_CONN_ITVL * itvl / 1000
+static int connection_attempts;
+
+static const struct ble_gap_conn_params dflt_conn_params = {
+ .scan_itvl = 0x0010,
+ .scan_window = 0x0010,
+ .itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN,
+ .itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX,
+ .latency = 0,
+ .supervision_timeout = 0x0100,
+ .min_ce_len = 0x0010,
+ .max_ce_len = 0x0300,
+};
+
+static void conn_param_update(struct os_event *ev);
+
+
+static int gap_conn_find_by_addr(const ble_addr_t *dev_addr,
+ struct ble_gap_conn_desc *out_desc)
+{
+ ble_addr_t addr = *dev_addr;
+
+ if (memcmp(BLE_ADDR_ANY, &peer_id_addr, 6) == 0) {
+ return ble_gap_conn_find_by_addr(&addr, out_desc);
+ }
+
+ if (BLE_ADDR_IS_RPA(&addr)) {
+ if(ble_addr_cmp(&peer_ota_addr, &addr) != 0) {
+ return -1;
+ }
+
+ return ble_gap_conn_find_by_addr(&addr, out_desc);
+ } else {
+ if(ble_addr_cmp(&peer_id_addr, &addr) != 0) {
+ return -1;
+ }
+
+ if (BLE_ADDR_IS_RPA(&peer_ota_addr)) {
+ /* Change addr type to ID addr */
+ addr.type |= 2;
+ }
+
+ return ble_gap_conn_find_by_addr(&addr, out_desc);
+ }
+}
+
+static int gap_event_cb(struct ble_gap_event *event, void *arg);
+
+static void supported_commands(u8_t *data, u16_t len)
+{
+ u8_t cmds[3];
+ struct gap_read_supported_commands_rp *rp = (void *) &cmds;
+
+ SYS_LOG_DBG("");
+
+ memset(cmds, 0, sizeof(cmds));
+
+ tester_set_bit(cmds, GAP_READ_SUPPORTED_COMMANDS);
+ tester_set_bit(cmds, GAP_READ_CONTROLLER_INDEX_LIST);
+ tester_set_bit(cmds, GAP_READ_CONTROLLER_INFO);
+ tester_set_bit(cmds, GAP_SET_CONNECTABLE);
+ tester_set_bit(cmds, GAP_SET_DISCOVERABLE);
+ tester_set_bit(cmds, GAP_SET_BONDABLE);
+ tester_set_bit(cmds, GAP_START_ADVERTISING);
+ tester_set_bit(cmds, GAP_STOP_ADVERTISING);
+ tester_set_bit(cmds, GAP_START_DISCOVERY);
+ tester_set_bit(cmds, GAP_STOP_DISCOVERY);
+ tester_set_bit(cmds, GAP_CONNECT);
+ tester_set_bit(cmds, GAP_DISCONNECT);
+ tester_set_bit(cmds, GAP_SET_IO_CAP);
+ tester_set_bit(cmds, GAP_PAIR);
+ tester_set_bit(cmds, GAP_UNPAIR);
+ tester_set_bit(cmds, GAP_PASSKEY_ENTRY);
+ tester_set_bit(cmds, GAP_PASSKEY_CONFIRM);
+ tester_set_bit(cmds, GAP_START_DIRECT_ADV);
+ tester_set_bit(cmds, GAP_CONN_PARAM_UPDATE);
+ tester_set_bit(cmds, GAP_OOB_LEGACY_SET_DATA);
+ tester_set_bit(cmds, GAP_OOB_SC_GET_LOCAL_DATA);
+ tester_set_bit(cmds, GAP_OOB_SC_SET_REMOTE_DATA);
+ tester_set_bit(cmds, GAP_SET_MITM);
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_READ_SUPPORTED_COMMANDS,
+ CONTROLLER_INDEX, (u8_t *) rp, sizeof(cmds));
+}
+
+static void controller_index_list(u8_t *data, u16_t len)
+{
+ struct gap_read_controller_index_list_rp *rp;
+ u8_t buf[sizeof(*rp) + 1];
+
+ SYS_LOG_DBG("");
+
+ rp = (void *) buf;
+
+ rp->num = 1;
+ rp->index[0] = CONTROLLER_INDEX;
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_READ_CONTROLLER_INDEX_LIST,
+ BTP_INDEX_NONE, (u8_t *) rp, sizeof(buf));
+}
+
+static int check_pub_addr_unassigned(void)
+{
+#ifdef ARCH_sim
+ return 0;
+#else
+ uint8_t zero_addr[BLE_DEV_ADDR_LEN] = { 0 };
+
+ return memcmp(MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR),
+ zero_addr, BLE_DEV_ADDR_LEN) == 0;
+#endif
+}
+
+static void controller_info(u8_t *data, u16_t len)
+{
+ struct gap_read_controller_info_rp rp;
+ u32_t supported_settings = 0;
+ ble_addr_t addr;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_hs_pvcy_set_our_irk(irk);
+ assert(rc == 0);
+
+ memset(&rp, 0, sizeof(rp));
+
+ /* Make sure we have proper identity address set (public preferred) */
+ rc = ble_hs_util_ensure_addr(1);
+ assert(rc == 0);
+ rc = ble_hs_id_copy_addr(BLE_ADDR_RANDOM, addr.val, NULL);
+ assert(rc == 0);
+
+ if (MYNEWT_VAL(BTTESTER_PRIVACY_MODE)) {
+ if (MYNEWT_VAL(BTTESTER_USE_NRPA)) {
+ own_addr_type = BLE_OWN_ADDR_RANDOM;
+ rc = ble_hs_id_gen_rnd(1, &addr);
+ assert(rc == 0);
+ rc = ble_hs_id_set_rnd(addr.val);
+ assert(rc == 0);
+ } else {
+ own_addr_type = BLE_OWN_ADDR_RPA_RANDOM_DEFAULT;
+ }
+ current_settings |= BIT(GAP_SETTINGS_PRIVACY);
+ supported_settings |= BIT(GAP_SETTINGS_PRIVACY);
+ memcpy(rp.address, addr.val, sizeof(rp.address));
+ } else {
+ if (check_pub_addr_unassigned()) {
+ own_addr_type = BLE_OWN_ADDR_RANDOM;
+ memcpy(rp.address, addr.val, sizeof(rp.address));
+ supported_settings |= BIT(GAP_SETTINGS_STATIC_ADDRESS);
+ current_settings |= BIT(GAP_SETTINGS_STATIC_ADDRESS);
+ } else {
+ own_addr_type = BLE_OWN_ADDR_PUBLIC;
+ memcpy(rp.address, MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR),
+ sizeof(rp.address));
+ }
+ }
+
+ supported_settings |= BIT(GAP_SETTINGS_POWERED);
+ supported_settings |= BIT(GAP_SETTINGS_CONNECTABLE);
+ supported_settings |= BIT(GAP_SETTINGS_BONDABLE);
+ supported_settings |= BIT(GAP_SETTINGS_LE);
+ supported_settings |= BIT(GAP_SETTINGS_ADVERTISING);
+ supported_settings |= BIT(GAP_SETTINGS_SC);
+
+ if (ble_hs_cfg.sm_bonding) {
+ current_settings |= BIT(GAP_SETTINGS_BONDABLE);
+ }
+ if (ble_hs_cfg.sm_sc) {
+ current_settings |= BIT(GAP_SETTINGS_SC);
+ }
+
+ rp.supported_settings = sys_cpu_to_le32(supported_settings);
+ rp.current_settings = sys_cpu_to_le32(current_settings);
+
+ memcpy(rp.name, CONTROLLER_NAME, sizeof(CONTROLLER_NAME));
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_READ_CONTROLLER_INFO,
+ CONTROLLER_INDEX, (u8_t *) &rp, sizeof(rp));
+}
+
+static struct ble_gap_adv_params adv_params = {
+ .conn_mode = BLE_GAP_CONN_MODE_NON,
+ .disc_mode = BLE_GAP_DISC_MODE_NON,
+};
+
+static void set_connectable(u8_t *data, u16_t len)
+{
+ const struct gap_set_connectable_cmd *cmd = (void *) data;
+ struct gap_set_connectable_rp rp;
+
+ SYS_LOG_DBG("");
+
+ if (cmd->connectable) {
+ current_settings |= BIT(GAP_SETTINGS_CONNECTABLE);
+ adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
+ } else {
+ current_settings &= ~BIT(GAP_SETTINGS_CONNECTABLE);
+ adv_params.conn_mode = BLE_GAP_CONN_MODE_NON;
+ }
+
+ rp.current_settings = sys_cpu_to_le32(current_settings);
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_SET_CONNECTABLE, CONTROLLER_INDEX,
+ (u8_t *) &rp, sizeof(rp));
+}
+
+static u8_t ad_flags = BLE_HS_ADV_F_BREDR_UNSUP;
+
+static void set_discoverable(u8_t *data, u16_t len)
+{
+ const struct gap_set_discoverable_cmd *cmd = (void *) data;
+ struct gap_set_discoverable_rp rp;
+
+ SYS_LOG_DBG("");
+
+ switch (cmd->discoverable) {
+ case GAP_NON_DISCOVERABLE:
+ ad_flags &= ~(BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_DISC_LTD);
+ adv_params.disc_mode = BLE_GAP_DISC_MODE_NON;
+ current_settings &= ~BIT(GAP_SETTINGS_DISCOVERABLE);
+ break;
+ case GAP_GENERAL_DISCOVERABLE:
+ ad_flags &= ~BLE_HS_ADV_F_DISC_LTD;
+ ad_flags |= BLE_HS_ADV_F_DISC_GEN;
+ adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
+ current_settings |= BIT(GAP_SETTINGS_DISCOVERABLE);
+ break;
+ case GAP_LIMITED_DISCOVERABLE:
+ ad_flags &= ~BLE_HS_ADV_F_DISC_GEN;
+ ad_flags |= BLE_HS_ADV_F_DISC_LTD;
+ adv_params.disc_mode = BLE_GAP_DISC_MODE_LTD;
+ current_settings |= BIT(GAP_SETTINGS_DISCOVERABLE);
+ break;
+ default:
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_DISCOVERABLE,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ return;
+ }
+
+ rp.current_settings = sys_cpu_to_le32(current_settings);
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_SET_DISCOVERABLE, CONTROLLER_INDEX,
+ (u8_t *) &rp, sizeof(rp));
+}
+
+static void set_bondable(const u8_t *data, u16_t len)
+{
+ const struct gap_set_bondable_cmd *cmd = (void *) data;
+ struct gap_set_bondable_rp rp;
+
+ SYS_LOG_DBG("");
+
+ ble_hs_cfg.sm_bonding = cmd->bondable;
+ if (ble_hs_cfg.sm_bonding) {
+ current_settings |= BIT(GAP_SETTINGS_BONDABLE);
+ } else {
+ current_settings &= ~BIT(GAP_SETTINGS_BONDABLE);
+ }
+
+ rp.current_settings = sys_cpu_to_le32(current_settings);
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_SET_BONDABLE, CONTROLLER_INDEX,
+ (u8_t *) &rp, sizeof(rp));
+}
+
+static struct bt_data ad[10] = {
+ BT_DATA(BLE_HS_ADV_TYPE_FLAGS, &ad_flags, sizeof(ad_flags)),
+};
+static struct bt_data sd[10];
+
+static int set_ad(const struct bt_data *ad, size_t ad_len,
+ u8_t *buf, u8_t *buf_len)
+{
+ int i;
+
+ for (i = 0; i < ad_len; i++) {
+ buf[(*buf_len)++] = ad[i].data_len + 1;
+ buf[(*buf_len)++] = ad[i].type;
+
+ memcpy(&buf[*buf_len], ad[i].data,
+ ad[i].data_len);
+ *buf_len += ad[i].data_len;
+ }
+
+ return 0;
+}
+
+static void start_advertising(const u8_t *data, u16_t len)
+{
+ const struct gap_start_advertising_cmd *cmd = (void *) data;
+ struct gap_start_advertising_rp rp;
+ int32_t duration_ms = BLE_HS_FOREVER;
+ uint8_t buf[BLE_HS_ADV_MAX_SZ];
+ uint8_t buf_len = 0;
+ u8_t adv_len, sd_len;
+ int err;
+
+ int i;
+
+ SYS_LOG_DBG("");
+
+ for (i = 0, adv_len = 1; i < cmd->adv_data_len; adv_len++) {
+ if (adv_len >= ARRAY_SIZE(ad)) {
+ SYS_LOG_ERR("ad[] Out of memory");
+ goto fail;
+ }
+
+ ad[adv_len].type = cmd->adv_data[i++];
+ ad[adv_len].data_len = cmd->adv_data[i++];
+ ad[adv_len].data = &cmd->adv_data[i];
+ i += ad[adv_len].data_len;
+ }
+
+ for (i = 0, sd_len = 0; i < cmd->scan_rsp_len; sd_len++) {
+ if (sd_len >= ARRAY_SIZE(sd)) {
+ SYS_LOG_ERR("sd[] Out of memory");
+ goto fail;
+ }
+
+ sd[sd_len].type = cmd->scan_rsp[i++];
+ sd[sd_len].data_len = cmd->scan_rsp[i++];
+ sd[sd_len].data = &cmd->scan_rsp[i];
+ i += sd[sd_len].data_len;
+ }
+
+ err = set_ad(ad, adv_len, buf, &buf_len);
+ if (err) {
+ goto fail;
+ }
+
+ err = ble_gap_adv_set_data(buf, buf_len);
+ if (err != 0) {
+ goto fail;
+ }
+
+ if (sd_len) {
+ buf_len = 0;
+
+ err = set_ad(sd, sd_len, buf, &buf_len);
+ if (err) {
+ SYS_LOG_ERR("Advertising failed: err %d", err);
+ goto fail;
+ }
+
+ err = ble_gap_adv_rsp_set_data(buf, buf_len);
+ if (err != 0) {
+ SYS_LOG_ERR("Advertising failed: err %d", err);
+ goto fail;
+ }
+ }
+
+ if (adv_params.disc_mode == BLE_GAP_DISC_MODE_LTD) {
+ duration_ms = MYNEWT_VAL(BTTESTER_LTD_ADV_TIMEOUT);
+ }
+
+ err = ble_gap_adv_start(own_addr_type, NULL, duration_ms,
+ &adv_params, gap_event_cb, NULL);
+ if (err) {
+ SYS_LOG_ERR("Advertising failed: err %d", err);
+ goto fail;
+ }
+
+ current_settings |= BIT(GAP_SETTINGS_ADVERTISING);
+ rp.current_settings = sys_cpu_to_le32(current_settings);
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_START_ADVERTISING, CONTROLLER_INDEX,
+ (u8_t *) &rp, sizeof(rp));
+ return;
+fail:
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_ADVERTISING, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static void stop_advertising(const u8_t *data, u16_t len)
+{
+ struct gap_stop_advertising_rp rp;
+
+ SYS_LOG_DBG("");
+
+ if (ble_gap_adv_stop() != 0) {
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_STOP_ADVERTISING,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ return;
+ }
+
+ current_settings &= ~BIT(GAP_SETTINGS_ADVERTISING);
+ rp.current_settings = sys_cpu_to_le32(current_settings);
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_STOP_ADVERTISING, CONTROLLER_INDEX,
+ (u8_t *) &rp, sizeof(rp));
+}
+
+static u8_t get_ad_flags(const u8_t *data, u8_t data_len)
+{
+ u8_t len, i;
+
+ /* Parse advertisement to get flags */
+ for (i = 0; i < data_len; i += len - 1) {
+ len = data[i++];
+ if (!len) {
+ break;
+ }
+
+ /* Check if field length is correct */
+ if (len > (data_len - i) || (data_len - i) < 1) {
+ break;
+ }
+
+ switch (data[i++]) {
+ case BLE_HS_ADV_TYPE_FLAGS:
+ return data[i];
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static u8_t discovery_flags;
+static struct os_mbuf *adv_buf;
+
+static void store_adv(const ble_addr_t *addr, s8_t rssi,
+ const u8_t *data, u8_t len)
+{
+ struct gap_device_found_ev *ev;
+
+ /* cleanup */
+ net_buf_simple_init(adv_buf, 0);
+
+ ev = net_buf_simple_add(adv_buf, sizeof(*ev));
+
+ memcpy(ev->address, addr->val, sizeof(ev->address));
+ ev->address_type = addr->type;
+ ev->rssi = rssi;
+ ev->flags = GAP_DEVICE_FOUND_FLAG_AD | GAP_DEVICE_FOUND_FLAG_RSSI;
+ ev->eir_data_len = len;
+ memcpy(net_buf_simple_add(adv_buf, len), data, len);
+}
+
+static void device_found(ble_addr_t *addr, s8_t rssi, u8_t evtype,
+ const u8_t *data, u8_t len)
+{
+ struct gap_device_found_ev *ev;
+ ble_addr_t a;
+
+ /* if General/Limited Discovery - parse Advertising data to get flags */
+ if (!(discovery_flags & GAP_DISCOVERY_FLAG_LE_OBSERVE) &&
+ (evtype != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP)) {
+ u8_t flags = get_ad_flags(data, len);
+
+ /* ignore non-discoverable devices */
+ if (!(flags & BLE_AD_DISCOV_MASK)) {
+ SYS_LOG_DBG("Non discoverable, skipping");
+ return;
+ }
+
+ /* if Limited Discovery - ignore general discoverable devices */
+ if ((discovery_flags & GAP_DISCOVERY_FLAG_LIMITED) &&
+ !(flags & BLE_HS_ADV_F_DISC_LTD)) {
+ SYS_LOG_DBG("General discoverable, skipping");
+ return;
+ }
+ }
+
+ /* attach Scan Response data */
+ if (evtype == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
+ /* skip if there is no pending advertisement */
+ if (!adv_buf->om_len) {
+ SYS_LOG_INF("No pending advertisement, skipping");
+ return;
+ }
+
+ ev = (void *) adv_buf->om_data;
+ a.type = ev->address_type;
+ memcpy(a.val, ev->address, sizeof(a.val));
+
+ /*
+ * in general, the Scan Response comes right after the
+ * Advertisement, but if not if send stored event and ignore
+ * this one
+ */
+ if (ble_addr_cmp(addr, &a)) {
+ SYS_LOG_INF("Address does not match, skipping");
+ goto done;
+ }
+
+ ev->eir_data_len += len;
+ ev->flags |= GAP_DEVICE_FOUND_FLAG_SD;
+
+ memcpy(net_buf_simple_add(adv_buf, len), data, len);
+
+ goto done;
+ }
+
+ /*
+ * if there is another pending advertisement, send it and store the
+ * current one
+ */
+ if (adv_buf->om_len) {
+ tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_FOUND,
+ CONTROLLER_INDEX, adv_buf->om_data,
+ adv_buf->om_len);
+ }
+
+ store_adv(addr, rssi, data, len);
+
+ /* if Active Scan and scannable event - wait for Scan Response */
+ if ((discovery_flags & GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN) &&
+ (evtype == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND ||
+ evtype == BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND)) {
+ SYS_LOG_DBG("Waiting for scan response");
+ return;
+ }
+done:
+ tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_FOUND,
+ CONTROLLER_INDEX, adv_buf->om_data, adv_buf->om_len);
+}
+
+static int discovery_cb(struct ble_gap_event *event, void *arg)
+{
+ if (event->type == BLE_GAP_EVENT_DISC) {
+ device_found(&event->disc.addr, event->disc.rssi,
+ event->disc.event_type, event->disc.data,
+ event->disc.length_data);
+ }
+
+ return 0;
+}
+
+static void start_discovery(const u8_t *data, u16_t len)
+{
+ const struct gap_start_discovery_cmd *cmd = (void *) data;
+ struct ble_gap_disc_params params = {0};
+ u8_t status;
+
+ SYS_LOG_DBG("");
+
+ /* only LE scan is supported */
+ if (cmd->flags & GAP_DISCOVERY_FLAG_BREDR) {
+ status = BTP_STATUS_FAILED;
+ goto reply;
+ }
+
+ params.passive = (cmd->flags & GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN) == 0;
+ params.limited = (cmd->flags & GAP_DISCOVERY_FLAG_LIMITED) > 0;
+ params.filter_duplicates = 1;
+
+ if (ble_gap_disc(own_addr_type, BLE_HS_FOREVER,
+ &params, discovery_cb, NULL) != 0) {
+ status = BTP_STATUS_FAILED;
+ goto reply;
+ }
+
+ net_buf_simple_init(adv_buf, 0);
+ discovery_flags = cmd->flags;
+
+ status = BTP_STATUS_SUCCESS;
+reply:
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_DISCOVERY, CONTROLLER_INDEX,
+ status);
+}
+
+static void stop_discovery(const u8_t *data, u16_t len)
+{
+ u8_t status = BTP_STATUS_SUCCESS;
+
+ SYS_LOG_DBG("");
+
+ if (ble_gap_disc_cancel() != 0) {
+ status = BTP_STATUS_FAILED;
+ }
+
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_STOP_DISCOVERY, CONTROLLER_INDEX,
+ status);
+}
+
+
+/* Bluetooth Core Spec v5.1 | Section 10.7.1
+ * If a privacy-enabled Peripheral, that has a stored bond,
+ * receives a resolvable private address, the Host may resolve
+ * the resolvable private address [...]
+ * If the resolution is successful, the Host may accept the connection.
+ * If the resolution procedure fails, then the Host shall disconnect
+ * with the error code "Authentication failure" [...]
+ */
+static void periph_privacy(struct ble_gap_conn_desc desc)
+{
+#if !MYNEWT_VAL(BTTESTER_PRIVACY_MODE)
+ return;
+#endif
+ int count;
+
+ SYS_LOG_DBG("");
+
+ ble_store_util_count(BLE_STORE_OBJ_TYPE_PEER_SEC, &count);
+ if (count > 0 && BLE_ADDR_IS_RPA(&desc.peer_id_addr)) {
+ SYS_LOG_DBG("Authentication failure, disconnecting");
+ ble_gap_terminate(desc.conn_handle, BLE_ERR_AUTH_FAIL);
+ }
+}
+
+static void device_connected_ev_send(struct os_event *ev)
+{
+ struct ble_gap_conn_desc desc;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = gap_conn_find_by_addr((ble_addr_t *)&connected_ev, &desc);
+ if (rc) {
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_CONNECTED,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ return;
+ }
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_CONNECTED,
+ CONTROLLER_INDEX, (u8_t *) &connected_ev,
+ sizeof(connected_ev));
+
+ periph_privacy(desc);
+}
+
+static void le_connected(u16_t conn_handle, int status)
+{
+ struct ble_gap_conn_desc desc;
+ ble_addr_t *addr;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ if (status != 0) {
+ return;
+ }
+
+ rc = ble_gap_conn_find(conn_handle, &desc);
+ if (rc) {
+ return;
+ }
+
+ peer_id_addr = desc.peer_id_addr;
+ peer_ota_addr = desc.peer_ota_addr;
+
+ addr = &desc.peer_id_addr;
+
+ memcpy(connected_ev.address, addr->val, sizeof(connected_ev.address));
+ connected_ev.address_type = addr->type;
+ connected_ev.conn_itvl = desc.conn_itvl;
+ connected_ev.conn_latency = desc.conn_latency;
+ connected_ev.supervision_timeout = desc.supervision_timeout;
+
+#if MYNEWT_VAL(BTTESTER_CONN_RETRY)
+ os_callout_reset(&connected_ev_co,
+ os_time_ms_to_ticks32(
+ CONNECTED_EV_DELAY_MS(desc.conn_itvl)));
+#else
+ tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_CONNECTED,
+ CONTROLLER_INDEX, (u8_t *) &connected_ev,
+ sizeof(connected_ev));
+#endif
+}
+
+static void le_disconnected(struct ble_gap_conn_desc *conn, int reason)
+{
+ struct gap_device_disconnected_ev ev;
+ ble_addr_t *addr = &conn->peer_ota_addr;
+
+ SYS_LOG_DBG("");
+
+#if MYNEWT_VAL(BTTESTER_CONN_RETRY)
+ int rc;
+
+ if ((reason == BLE_HS_HCI_ERR(BLE_ERR_CONN_ESTABLISHMENT)) &&
+ os_callout_queued(&connected_ev_co)) {
+ if (connection_attempts < MYNEWT_VAL(BTTESTER_CONN_RETRY)) {
+ os_callout_stop(&connected_ev_co);
+
+ /* try connecting again */
+ rc = ble_gap_connect(own_addr_type, addr, 0,
+ &dflt_conn_params, gap_event_cb,
+ NULL);
+
+ if (rc == 0) {
+ connection_attempts++;
+ return;
+ }
+ }
+ } else if (os_callout_queued(&connected_ev_co)) {
+ os_callout_stop(&connected_ev_co);
+ return;
+ }
+#endif
+
+ connection_attempts = 0;
+ memset(&connected_ev, 0, sizeof(connected_ev));
+
+ memcpy(ev.address, addr->val, sizeof(ev.address));
+ ev.address_type = addr->type;
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_DISCONNECTED,
+ CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
+}
+
+static void auth_passkey_oob(u16_t conn_handle)
+{
+ struct ble_gap_conn_desc desc;
+ struct ble_sm_io pk;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find(conn_handle, &desc);
+ if (rc) {
+ return;
+ }
+
+ memcpy(pk.oob, oob, sizeof(oob));
+ pk.action = BLE_SM_IOACT_OOB;
+
+ rc = ble_sm_inject_io(conn_handle, &pk);
+ assert(rc == 0);
+}
+
+static void auth_passkey_display(u16_t conn_handle, unsigned int passkey)
+{
+ struct ble_gap_conn_desc desc;
+ struct gap_passkey_display_ev ev;
+ ble_addr_t *addr;
+ struct ble_sm_io pk;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find(conn_handle, &desc);
+ if (rc) {
+ return;
+ }
+
+ rc = ble_hs_hci_util_rand(&pk.passkey, sizeof(pk.passkey));
+ assert(rc == 0);
+ /* Max value is 999999 */
+ pk.passkey %= 1000000;
+ pk.action = BLE_SM_IOACT_DISP;
+
+ rc = ble_sm_inject_io(conn_handle, &pk);
+ assert(rc == 0);
+
+ addr = &desc.peer_ota_addr;
+
+ memcpy(ev.address, addr->val, sizeof(ev.address));
+ ev.address_type = addr->type;
+ ev.passkey = sys_cpu_to_le32(pk.passkey);
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_DISPLAY,
+ CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
+}
+
+static void auth_passkey_entry(u16_t conn_handle)
+{
+ struct ble_gap_conn_desc desc;
+ struct gap_passkey_entry_req_ev ev;
+ ble_addr_t *addr;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find(conn_handle, &desc);
+ if (rc) {
+ return;
+ }
+
+ addr = &desc.peer_ota_addr;
+
+ memcpy(ev.address, addr->val, sizeof(ev.address));
+ ev.address_type = addr->type;
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_ENTRY_REQ,
+ CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
+}
+
+static void auth_passkey_numcmp(u16_t conn_handle, unsigned int passkey)
+{
+ struct ble_gap_conn_desc desc;
+ struct gap_passkey_confirm_req_ev ev;
+ ble_addr_t *addr;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find(conn_handle, &desc);
+ if (rc) {
+ return;
+ }
+
+ addr = &desc.peer_ota_addr;
+
+ memcpy(ev.address, addr->val, sizeof(ev.address));
+ ev.address_type = addr->type;
+ ev.passkey = sys_cpu_to_le32(passkey);
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_CONFIRM_REQ,
+ CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
+}
+
+static void auth_passkey_oob_sc(u16_t conn_handle)
+{
+ int rc;
+ struct ble_sm_io pk;
+
+ SYS_LOG_DBG("");
+
+ memset(&pk, 0, sizeof(pk));
+
+ pk.oob_sc_data.local = &oob_data_local;
+
+ if (ble_hs_cfg.sm_oob_data_flag) {
+ pk.oob_sc_data.remote = &oob_data_remote;
+ }
+
+ pk.action = BLE_SM_IOACT_OOB_SC;
+ rc = ble_sm_inject_io(conn_handle, &pk);
+ if (rc != 0) {
+ console_printf("error providing oob; rc=%d\n", rc);
+ }
+}
+
+static void le_passkey_action(u16_t conn_handle,
+ struct ble_gap_passkey_params *params)
+{
+ SYS_LOG_DBG("");
+
+ switch (params->action) {
+ case BLE_SM_IOACT_NONE:
+ break;
+ case BLE_SM_IOACT_OOB:
+ auth_passkey_oob(conn_handle);
+ break;
+ case BLE_SM_IOACT_INPUT:
+ auth_passkey_entry(conn_handle);
+ break;
+ case BLE_SM_IOACT_DISP:
+ auth_passkey_display(conn_handle, params->numcmp);
+ break;
+ case BLE_SM_IOACT_NUMCMP:
+ auth_passkey_numcmp(conn_handle, params->numcmp);
+ break;
+ case BLE_SM_IOACT_OOB_SC:
+ auth_passkey_oob_sc(conn_handle);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void le_identity_resolved(u16_t conn_handle)
+{
+ struct ble_gap_conn_desc desc;
+ struct gap_identity_resolved_ev ev;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find(conn_handle, &desc);
+ if (rc) {
+ return;
+ }
+
+ peer_id_addr = desc.peer_id_addr;
+ peer_ota_addr = desc.peer_ota_addr;
+
+ ev.address_type = desc.peer_ota_addr.type;
+ memcpy(ev.address, desc.peer_ota_addr.val, sizeof(ev.address));
+
+ ev.identity_address_type = desc.peer_id_addr.type;
+ memcpy(ev.identity_address, desc.peer_id_addr.val,
+ sizeof(ev.identity_address));
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_EV_IDENTITY_RESOLVED,
+ CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
+}
+
+static void le_conn_param_update(struct ble_gap_conn_desc *desc)
+{
+ struct gap_conn_param_update_ev ev;
+
+ SYS_LOG_DBG("");
+
+ ev.address_type = desc->peer_ota_addr.type;
+ memcpy(ev.address, desc->peer_ota_addr.val, sizeof(ev.address));
+
+ ev.conn_itvl = desc->conn_itvl;
+ ev.conn_latency = desc->conn_latency;
+ ev.supervision_timeout = desc->supervision_timeout;
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_EV_CONN_PARAM_UPDATE,
+ CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
+}
+
+static void le_encryption_changed(struct ble_gap_conn_desc *desc)
+{
+ struct gap_sec_level_changed_ev ev;
+
+ SYS_LOG_DBG("");
+
+ encrypted = (bool) desc->sec_state.encrypted;
+
+ ev.address_type = desc->peer_ota_addr.type;
+ memcpy(ev.address, desc->peer_ota_addr.val, sizeof(ev.address));
+ ev.level = 0;
+
+ if (desc->sec_state.encrypted) {
+ if (desc->sec_state.authenticated) {
+ if (desc->sec_state.key_size == 16) {
+ ev.level = 3;
+ } else {
+ ev.level = 2;
+ }
+ } else {
+ ev.level = 1;
+ }
+ }
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_EV_SEC_LEVEL_CHANGED,
+ CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
+}
+
+static void print_bytes(const uint8_t *bytes, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ console_printf("%s0x%02x", i != 0 ? ":" : "", bytes[i]);
+ }
+}
+
+static void print_mbuf(const struct os_mbuf *om)
+{
+ int colon;
+
+ colon = 0;
+ while (om != NULL) {
+ if (colon) {
+ console_printf(":");
+ } else {
+ colon = 1;
+ }
+ print_bytes(om->om_data, om->om_len);
+ om = SLIST_NEXT(om, om_next);
+ }
+}
+
+static void print_addr(const void *addr)
+{
+ const uint8_t *u8p;
+
+ u8p = addr;
+ console_printf("%02x:%02x:%02x:%02x:%02x:%02x",
+ u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
+}
+
+static void print_conn_desc(const struct ble_gap_conn_desc *desc)
+{
+ console_printf("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);
+ console_printf(" our_id_addr_type=%d our_id_addr=",
+ desc->our_id_addr.type);
+ print_addr(desc->our_id_addr.val);
+ console_printf(" peer_ota_addr_type=%d peer_ota_addr=",
+ desc->peer_ota_addr.type);
+ print_addr(desc->peer_ota_addr.val);
+ console_printf(" peer_id_addr_type=%d peer_id_addr=",
+ desc->peer_id_addr.type);
+ print_addr(desc->peer_id_addr.val);
+ console_printf(" conn_itvl=%d conn_latency=%d supervision_timeout=%d "
+ "key_sz=%d encrypted=%d authenticated=%d bonded=%d\n",
+ desc->conn_itvl, desc->conn_latency,
+ desc->supervision_timeout,
+ desc->sec_state.key_size,
+ desc->sec_state.encrypted,
+ desc->sec_state.authenticated,
+ desc->sec_state.bonded);
+}
+
+static void adv_complete(void)
+{
+ struct gap_new_settings_ev ev;
+
+ current_settings &= ~BIT(GAP_SETTINGS_ADVERTISING);
+ ev.current_settings = sys_cpu_to_le32(current_settings);
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_EV_NEW_SETTINGS, CONTROLLER_INDEX,
+ (u8_t *) &ev, sizeof(ev));
+}
+
+static int gap_event_cb(struct ble_gap_event *event, void *arg)
+{
+ struct ble_gap_conn_desc desc;
+ int rc;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_ADV_COMPLETE:
+ console_printf("advertising complete; reason=%d\n",
+ event->adv_complete.reason);
+ break;
+ case BLE_GAP_EVENT_CONNECT:
+ console_printf("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);
+ print_conn_desc(&desc);
+ }
+
+ if (desc.role == BLE_GAP_ROLE_SLAVE) {
+ adv_complete();
+ }
+
+ le_connected(event->connect.conn_handle,
+ event->connect.status);
+ break;
+ case BLE_GAP_EVENT_DISCONNECT:
+ console_printf("disconnect; reason=%d ", event->disconnect.reason);
+ print_conn_desc(&event->disconnect.conn);
+ le_disconnected(&event->disconnect.conn,
+ event->disconnect.reason);
+ break;
+ case BLE_GAP_EVENT_ENC_CHANGE:
+ console_printf("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);
+ le_encryption_changed(&desc);
+ break;
+ case BLE_GAP_EVENT_PASSKEY_ACTION:
+ console_printf("passkey action event; action=%d",
+ event->passkey.params.action);
+ if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
+ console_printf(" numcmp=%lu",
+ (unsigned long)event->passkey.params.numcmp);
+ }
+ console_printf("\n");
+ le_passkey_action(event->passkey.conn_handle,
+ &event->passkey.params);
+ break;
+ case BLE_GAP_EVENT_IDENTITY_RESOLVED:
+ console_printf("identity resolved ");
+ rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &desc);
+ assert(rc == 0);
+ print_conn_desc(&desc);
+ le_identity_resolved(event->identity_resolved.conn_handle);
+ break;
+ case BLE_GAP_EVENT_NOTIFY_RX:
+ console_printf("notification rx event; attr_handle=%d indication=%d "
+ "len=%d data=",
+ event->notify_rx.attr_handle,
+ event->notify_rx.indication,
+ OS_MBUF_PKTLEN(event->notify_rx.om));
+
+ print_mbuf(event->notify_rx.om);
+ console_printf("\n");
+ tester_gatt_notify_rx_ev(event->notify_rx.conn_handle,
+ event->notify_rx.attr_handle,
+ event->notify_rx.indication,
+ event->notify_rx.om);
+ break;
+ case BLE_GAP_EVENT_SUBSCRIBE:
+ console_printf("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);
+ tester_gatt_subscribe_ev(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);
+ break;
+ case BLE_GAP_EVENT_REPEAT_PAIRING:
+ console_printf("repeat pairing event; conn_handle=%d "
+ "cur_key_sz=%d cur_auth=%d cur_sc=%d "
+ "new_key_sz=%d new_auth=%d new_sc=%d "
+ "new_bonding=%d\n",
+ event->repeat_pairing.conn_handle,
+ event->repeat_pairing.cur_key_size,
+ event->repeat_pairing.cur_authenticated,
+ event->repeat_pairing.cur_sc,
+ event->repeat_pairing.new_key_size,
+ event->repeat_pairing.new_authenticated,
+ event->repeat_pairing.new_sc,
+ event->repeat_pairing.new_bonding);
+ rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
+ assert(rc == 0);
+ rc = ble_store_util_delete_peer(&desc.peer_id_addr);
+ assert(rc == 0);
+ return BLE_GAP_REPEAT_PAIRING_RETRY;
+ case BLE_GAP_EVENT_CONN_UPDATE:
+ console_printf("connection update event; status=%d ",
+ event->conn_update.status);
+ rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
+ assert(rc == 0);
+ print_conn_desc(&desc);
+ le_conn_param_update(&desc);
+ break;
+ case BLE_GAP_EVENT_CONN_UPDATE_REQ:
+ console_printf("connection update request event; "
+ "conn_handle=%d itvl_min=%d itvl_max=%d "
+ "latency=%d supervision_timoeut=%d "
+ "min_ce_len=%d max_ce_len=%d\n",
+ event->conn_update_req.conn_handle,
+ event->conn_update_req.peer_params->itvl_min,
+ event->conn_update_req.peer_params->itvl_max,
+ event->conn_update_req.peer_params->latency,
+ event->conn_update_req.peer_params->supervision_timeout,
+ event->conn_update_req.peer_params->min_ce_len,
+ event->conn_update_req.peer_params->max_ce_len);
+
+ *event->conn_update_req.self_params =
+ *event->conn_update_req.peer_params;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void connect(const u8_t *data, u16_t len)
+{
+ u8_t status = BTP_STATUS_SUCCESS;
+
+ SYS_LOG_DBG("");
+
+ if (ble_gap_connect(own_addr_type, (ble_addr_t *) data, 0,
+ &dflt_conn_params, gap_event_cb, NULL)) {
+ status = BTP_STATUS_FAILED;
+ }
+
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_CONNECT, CONTROLLER_INDEX, status);
+}
+
+static void disconnect(const u8_t *data, u16_t len)
+{
+ struct ble_gap_conn_desc desc;
+ u8_t status;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = gap_conn_find_by_addr((ble_addr_t *)data, &desc);
+ if (rc) {
+ status = BTP_STATUS_FAILED;
+ goto rsp;
+ }
+
+ if (ble_gap_terminate(desc.conn_handle, BLE_ERR_REM_USER_CONN_TERM)) {
+ status = BTP_STATUS_FAILED;
+ } else {
+ status = BTP_STATUS_SUCCESS;
+ }
+
+rsp:
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_DISCONNECT, CONTROLLER_INDEX,
+ status);
+}
+
+static void set_io_cap(const u8_t *data, u16_t len)
+{
+ const struct gap_set_io_cap_cmd *cmd = (void *) data;
+ u8_t status;
+
+ SYS_LOG_DBG("");
+
+ switch (cmd->io_cap) {
+ case GAP_IO_CAP_DISPLAY_ONLY:
+ ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_ONLY;
+ ble_hs_cfg.sm_mitm = 1;
+ break;
+ case GAP_IO_CAP_KEYBOARD_DISPLAY:
+ ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_KEYBOARD_DISP;
+ ble_hs_cfg.sm_mitm = 1;
+ break;
+ case GAP_IO_CAP_NO_INPUT_OUTPUT:
+ ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_NO_IO;
+ ble_hs_cfg.sm_mitm = 0;
+ break;
+ case GAP_IO_CAP_KEYBOARD_ONLY:
+ ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_KEYBOARD_ONLY;
+ ble_hs_cfg.sm_mitm = 1;
+ break;
+ case GAP_IO_CAP_DISPLAY_YESNO:
+ ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_YES_NO;
+ ble_hs_cfg.sm_mitm = 1;
+ break;
+ default:
+ status = BTP_STATUS_FAILED;
+ goto rsp;
+ }
+
+ status = BTP_STATUS_SUCCESS;
+
+rsp:
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_IO_CAP, CONTROLLER_INDEX,
+ status);
+}
+
+static void pair(const u8_t *data, u16_t len)
+{
+ struct ble_gap_conn_desc desc;
+ u8_t status;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = gap_conn_find_by_addr((ble_addr_t *)data, &desc);
+ if (rc) {
+ status = BTP_STATUS_FAILED;
+ goto rsp;
+ }
+
+ if (ble_gap_security_initiate(desc.conn_handle)) {
+ status = BTP_STATUS_FAILED;
+ goto rsp;
+ }
+
+ status = BTP_STATUS_SUCCESS;
+
+rsp:
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_PAIR, CONTROLLER_INDEX, status);
+}
+
+static void unpair(const u8_t *data, u16_t len)
+{
+ u8_t status;
+ int err;
+
+ SYS_LOG_DBG("");
+
+ err = ble_gap_unpair((ble_addr_t *) data);
+ status = (uint8_t) (err != 0 ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_UNPAIR, CONTROLLER_INDEX, status);
+}
+
+static void passkey_entry(const u8_t *data, u16_t len)
+{
+ const struct gap_passkey_entry_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc desc;
+ struct ble_sm_io pk;
+ u8_t status;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = gap_conn_find_by_addr((ble_addr_t *)data, &desc);
+ if (rc) {
+ status = BTP_STATUS_FAILED;
+ goto rsp;
+ }
+
+ pk.action = BLE_SM_IOACT_INPUT;
+ pk.passkey = sys_le32_to_cpu(cmd->passkey);
+
+ rc = ble_sm_inject_io(desc.conn_handle, &pk);
+ if (rc) {
+ status = BTP_STATUS_FAILED;
+ goto rsp;
+ }
+
+ status = BTP_STATUS_SUCCESS;
+
+rsp:
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_PASSKEY_ENTRY, CONTROLLER_INDEX,
+ status);
+}
+
+static void passkey_confirm(const u8_t *data, u16_t len)
+{
+ const struct gap_passkey_confirm_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc desc;
+ struct ble_sm_io pk;
+ u8_t status;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = gap_conn_find_by_addr((ble_addr_t *)data, &desc);
+ if (rc) {
+ status = BTP_STATUS_FAILED;
+ goto rsp;
+ }
+
+ pk.action = BLE_SM_IOACT_NUMCMP;
+ pk.numcmp_accept = cmd->match;
+
+ rc = ble_sm_inject_io(desc.conn_handle, &pk);
+ if (rc) {
+ console_printf("sm inject io failed");
+ status = BTP_STATUS_FAILED;
+ goto rsp;
+ }
+
+ status = BTP_STATUS_SUCCESS;
+
+rsp:
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_PASSKEY_CONFIRM, CONTROLLER_INDEX,
+ status);
+}
+
+static void start_direct_adv(const u8_t *data, u16_t len)
+{
+ const struct gap_start_direct_adv_cmd *cmd = (void *) data;
+ struct gap_start_advertising_rp rp;
+ static struct ble_gap_adv_params adv_params = {
+ .conn_mode = BLE_GAP_CONN_MODE_DIR,
+ };
+ int err;
+
+ SYS_LOG_DBG("");
+
+ adv_params.high_duty_cycle = cmd->high_duty;
+
+ err = ble_gap_adv_start(own_addr_type, (ble_addr_t *)data,
+ BLE_HS_FOREVER, &adv_params,
+ gap_event_cb, NULL);
+ if (err) {
+ SYS_LOG_ERR("Advertising failed: err %d", err);
+ goto fail;
+ }
+
+ current_settings |= BIT(GAP_SETTINGS_ADVERTISING);
+ rp.current_settings = sys_cpu_to_le32(current_settings);
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_START_DIRECT_ADV, CONTROLLER_INDEX,
+ (u8_t *) &rp, sizeof(rp));
+ return;
+fail:
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_DIRECT_ADV, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static void conn_param_update_cb(uint16_t conn_handle, int status, void *arg)
+{
+ console_printf("conn param update complete; conn_handle=%d status=%d\n",
+ conn_handle, status);
+}
+
+static int conn_param_update_slave(u16_t conn_handle,
+ const struct gap_conn_param_update_cmd *cmd)
+{
+ int rc;
+ struct ble_l2cap_sig_update_params params;
+
+ params.itvl_min = cmd->conn_itvl_min;
+ params.itvl_max = cmd->conn_itvl_max;
+ params.slave_latency = cmd->conn_latency;
+ params.timeout_multiplier = cmd->supervision_timeout;
+
+ rc = ble_l2cap_sig_update(conn_handle, &params,
+ conn_param_update_cb, NULL);
+ if (rc) {
+ SYS_LOG_ERR("Failed to send update params: rc=%d", rc);
+ }
+
+ return 0;
+}
+
+static int conn_param_update_master(u16_t conn_handle,
+ const struct gap_conn_param_update_cmd *cmd)
+{
+ int rc;
+ struct ble_gap_upd_params params;
+
+ params.itvl_min = cmd->conn_itvl_min;
+ params.itvl_max = cmd->conn_itvl_max;
+ params.latency = cmd->conn_latency;
+ params.supervision_timeout = cmd->supervision_timeout;
+ params.min_ce_len = 0;
+ params.max_ce_len = 0;
+ rc = ble_gap_update_params(conn_handle, &params);
+ if (rc) {
+ SYS_LOG_ERR("Failed to send update params: rc=%d", rc);
+ }
+
+ return rc;
+}
+
+static void conn_param_update(struct os_event *ev)
+{
+ struct ble_gap_conn_desc desc;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = gap_conn_find_by_addr((ble_addr_t *)&update_params, &desc);
+ if (rc) {
+ goto rsp;
+ }
+
+ if ((desc.conn_itvl >= update_params.conn_itvl_min) &&
+ (desc.conn_itvl <= update_params.conn_itvl_max) &&
+ (desc.conn_latency == update_params.conn_latency) &&
+ (desc.supervision_timeout == update_params.supervision_timeout)) {
+ goto rsp;
+ }
+
+ if (desc.role == BLE_GAP_ROLE_MASTER) {
+ rc = conn_param_update_master(desc.conn_handle, &update_params);
+ } else {
+ rc = conn_param_update_slave(desc.conn_handle, &update_params);
+ }
+
+ if (rc == 0) {
+ return;
+ }
+
+rsp:
+ SYS_LOG_ERR("Conn param update fail; rc=%d", rc);
+}
+
+static void conn_param_update_async(const u8_t *data, u16_t len)
+{
+ const struct gap_conn_param_update_cmd *cmd = (void *) data;
+ update_params = *cmd;
+
+ os_callout_reset(&update_params_co, 0);
+
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_CONN_PARAM_UPDATE, CONTROLLER_INDEX,
+ BTP_STATUS_SUCCESS);
+}
+
+static void oob_legacy_set_data(const u8_t *data, u16_t len)
+{
+ const struct gap_oob_legacy_set_data_cmd *cmd = (void *) data;
+
+ ble_hs_cfg.sm_oob_data_flag = 1;
+ memcpy(oob, cmd->oob_data, sizeof(oob));
+
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_OOB_LEGACY_SET_DATA,
+ CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
+}
+
+static void oob_sc_get_local_data(const u8_t *data, u16_t len)
+{
+ struct gap_oob_sc_get_local_data_rp rp;
+
+ memcpy(rp.r, oob_data_local.r, 16);
+ memcpy(rp.c, oob_data_local.c, 16);
+
+ tester_send(BTP_SERVICE_ID_GAP, GAP_OOB_SC_GET_LOCAL_DATA,
+ CONTROLLER_INDEX, (u8_t *) &rp, sizeof(rp));
+}
+
+static void oob_sc_set_remote_data(const u8_t *data, u16_t len)
+{
+ const struct gap_oob_sc_set_remote_data_cmd *cmd = (void *) data;
+
+ ble_hs_cfg.sm_oob_data_flag = 1;
+ memcpy(oob_data_remote.r, cmd->r, 16);
+ memcpy(oob_data_remote.c, cmd->c, 16);
+
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_OOB_SC_SET_REMOTE_DATA,
+ CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
+}
+
+static void set_mitm(const u8_t *data, u16_t len)
+{
+ const struct gap_set_mitm_cmd *cmd = (void *) data;
+
+ ble_hs_cfg.sm_mitm = cmd->mitm;
+
+ tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_MITM,
+ CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
+}
+
+void tester_handle_gap(u8_t opcode, u8_t index, u8_t *data,
+ u16_t len)
+{
+ switch (opcode) {
+ case GAP_READ_SUPPORTED_COMMANDS:
+ case GAP_READ_CONTROLLER_INDEX_LIST:
+ if (index != BTP_INDEX_NONE){
+ tester_rsp(BTP_SERVICE_ID_GAP, opcode, index,
+ BTP_STATUS_FAILED);
+ return;
+ }
+ break;
+ default:
+ if (index != CONTROLLER_INDEX){
+ tester_rsp(BTP_SERVICE_ID_GAP, opcode, index,
+ BTP_STATUS_FAILED);
+ return;
+ }
+ break;
+ }
+
+ switch (opcode) {
+ case GAP_READ_SUPPORTED_COMMANDS:
+ supported_commands(data, len);
+ return;
+ case GAP_READ_CONTROLLER_INDEX_LIST:
+ controller_index_list(data, len);
+ return;
+ case GAP_READ_CONTROLLER_INFO:
+ controller_info(data, len);
+ return;
+ case GAP_SET_CONNECTABLE:
+ set_connectable(data, len);
+ return;
+ case GAP_SET_DISCOVERABLE:
+ set_discoverable(data, len);
+ return;
+ case GAP_SET_BONDABLE:
+ set_bondable(data, len);
+ return;
+ case GAP_START_ADVERTISING:
+ start_advertising(data, len);
+ return;
+ case GAP_STOP_ADVERTISING:
+ stop_advertising(data, len);
+ return;
+ case GAP_START_DISCOVERY:
+ start_discovery(data, len);
+ return;
+ case GAP_STOP_DISCOVERY:
+ stop_discovery(data, len);
+ return;
+ case GAP_CONNECT:
+ connect(data, len);
+ return;
+ case GAP_DISCONNECT:
+ disconnect(data, len);
+ return;
+ case GAP_SET_IO_CAP:
+ set_io_cap(data, len);
+ return;
+ case GAP_PAIR:
+ pair(data, len);
+ return;
+ case GAP_UNPAIR:
+ unpair(data, len);
+ return;
+ case GAP_PASSKEY_ENTRY:
+ passkey_entry(data, len);
+ return;
+ case GAP_PASSKEY_CONFIRM:
+ passkey_confirm(data, len);
+ return;
+ case GAP_START_DIRECT_ADV:
+ start_direct_adv(data, len);
+ return;
+ case GAP_CONN_PARAM_UPDATE:
+ conn_param_update_async(data, len);
+ return;
+ case GAP_OOB_LEGACY_SET_DATA:
+ oob_legacy_set_data(data, len);
+ return;
+ case GAP_OOB_SC_GET_LOCAL_DATA:
+ oob_sc_get_local_data(data, len);
+ return;
+ case GAP_OOB_SC_SET_REMOTE_DATA:
+ oob_sc_set_remote_data(data, len);
+ return;
+ case GAP_SET_MITM:
+ set_mitm(data, len);
+ return;
+ default:
+ tester_rsp(BTP_SERVICE_ID_GAP, opcode, index,
+ BTP_STATUS_UNKNOWN_CMD);
+ return;
+ }
+}
+
+static void tester_init_gap_cb(int err)
+{
+ if (err) {
+ tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE,
+ BTP_INDEX_NONE, BTP_STATUS_FAILED);
+ return;
+ }
+
+ current_settings = 0;
+ current_settings |= BIT(GAP_SETTINGS_POWERED);
+ current_settings |= BIT(GAP_SETTINGS_LE);
+
+ os_callout_init(&update_params_co, os_eventq_dflt_get(),
+ conn_param_update, NULL);
+
+ os_callout_init(&connected_ev_co, os_eventq_dflt_get(),
+ device_connected_ev_send, NULL);
+
+ tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE, BTP_INDEX_NONE,
+ BTP_STATUS_SUCCESS);
+}
+
+u8_t tester_init_gap(void)
+{
+#if MYNEWT_VAL(BLE_SM_SC)
+ int rc;
+
+ rc = ble_sm_sc_oob_generate_data(&oob_data_local);
+ if (rc) {
+ console_printf("Error: generating oob data; reason=%d\n", rc);
+ return BTP_STATUS_FAILED;
+ }
+#endif
+
+ adv_buf = NET_BUF_SIMPLE(ADV_BUF_LEN);
+
+ tester_init_gap_cb(BTP_STATUS_SUCCESS);
+ return BTP_STATUS_SUCCESS;
+}
+
+u8_t tester_unregister_gap(void)
+{
+ return BTP_STATUS_SUCCESS;
+}
diff --git a/src/libs/mynewt-nimble/apps/bttester/src/gatt.c b/src/libs/mynewt-nimble/apps/bttester/src/gatt.c
new file mode 100644
index 00000000..7e7d1d3b
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/src/gatt.c
@@ -0,0 +1,2098 @@
+/*
+ * 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.
+ */
+
+/* gatt.c - Bluetooth GATT Server Tester */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "host/ble_gap.h"
+#include "host/ble_gatt.h"
+#include "console/console.h"
+#include "services/gatt/ble_svc_gatt.h"
+#include "../../../nimble/host/src/ble_att_priv.h"
+#include "../../../nimble/host/src/ble_gatt_priv.h"
+
+#include "bttester.h"
+
+#define CONTROLLER_INDEX 0
+#define MAX_BUFFER_SIZE 2048
+
+/* 0000xxxx-8c26-476f-89a7-a108033a69c7 */
+#define PTS_UUID_DECLARE(uuid16) \
+ ((const ble_uuid_t *) (&(ble_uuid128_t) BLE_UUID128_INIT( \
+ 0xc7, 0x69, 0x3a, 0x03, 0x08, 0xa1, 0xa7, 0x89, \
+ 0x6f, 0x47, 0x26, 0x8c, uuid16, uuid16 >> 8, 0x00, 0x00 \
+ )))
+
+/* 0000xxxx-8c26-476f-89a7-a108033a69c6 */
+#define PTS_UUID_DECLARE_ALT(uuid16) \
+ ((const ble_uuid_t *) (&(ble_uuid128_t) BLE_UUID128_INIT( \
+ 0xc6, 0x69, 0x3a, 0x03, 0x08, 0xa1, 0xa7, 0x89, \
+ 0x6f, 0x47, 0x26, 0x8c, uuid16, uuid16 >> 8, 0x00, 0x00 \
+ )))
+
+#define PTS_SVC 0x0001
+#define PTS_CHR_READ 0x0002
+#define PTS_CHR_WRITE 0x0003
+#define PTS_CHR_RELIABLE_WRITE 0x0004
+#define PTS_CHR_WRITE_NO_RSP 0x0005
+#define PTS_CHR_READ_WRITE 0x0006
+#define PTS_CHR_READ_WRITE_ENC 0x0007
+#define PTS_CHR_READ_WRITE_AUTHEN 0x0008
+#define PTS_DSC_READ 0x0009
+#define PTS_DSC_WRITE 0x000a
+#define PTS_DSC_READ_WRITE 0x000b
+#define PTS_CHR_NOTIFY 0x0025
+#define PTS_LONG_CHR_READ_WRITE 0x0015
+#define PTS_LONG_CHR_READ_WRITE_ALT 0x0016
+#define PTS_LONG_DSC_READ_WRITE 0x001b
+#define PTS_INC_SVC 0x001e
+#define PTS_CHR_READ_WRITE_ALT 0x001f
+
+static uint8_t gatt_svr_pts_static_long_val[300];
+static uint8_t gatt_svr_pts_static_val[30];
+static uint8_t gatt_svr_pts_static_short_val;
+static u8_t notify_state;
+static u8_t indicate_state;
+static uint16_t myconn_handle;
+static struct os_callout notify_tx_timer;
+uint16_t notify_handle;
+uint8_t notify_value = 90;
+
+struct find_attr_data {
+ ble_uuid_any_t *uuid;
+ int attr_type;
+ void *ptr;
+ uint16_t handle;
+};
+
+static int
+gatt_svr_read_write_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_read_write_auth_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_read_write_enc_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_dsc_read_write_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_write_no_rsp_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_rel_write_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_read_write_long_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_dsc_read_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static int
+gatt_svr_dsc_read_write_long_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static const struct ble_gatt_svc_def gatt_svr_inc_svcs[] = {
+ {
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = PTS_UUID_DECLARE(PTS_INC_SVC),
+ .characteristics = (struct ble_gatt_chr_def[]) {{
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ALT),
+ .access_cb = gatt_svr_read_write_test,
+ .flags = BLE_GATT_CHR_F_WRITE |
+ BLE_GATT_CHR_F_READ,
+ }, {
+ 0,
+ } },
+ },
+
+ {
+ 0, /* No more services. */
+ },
+};
+
+static const struct ble_gatt_svc_def *inc_svcs[] = {
+ &gatt_svr_inc_svcs[0],
+ NULL,
+};
+
+static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
+ {
+ /*** Service: PTS test. */
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = PTS_UUID_DECLARE(PTS_SVC),
+ .includes = inc_svcs,
+ .characteristics = (struct ble_gatt_chr_def[]) { {
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE),
+ .access_cb = gatt_svr_read_write_test,
+ .flags = BLE_GATT_CHR_F_READ |
+ BLE_GATT_CHR_F_WRITE,
+ .descriptors = (struct ble_gatt_dsc_def[]) { {
+ .uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE),
+ .access_cb = gatt_svr_dsc_read_write_test,
+ .att_flags = BLE_ATT_F_READ |
+ BLE_ATT_F_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE),
+ .access_cb = gatt_svr_dsc_read_write_long_test,
+ .att_flags = BLE_ATT_F_READ |
+ BLE_ATT_F_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_DSC_READ),
+ .access_cb = gatt_svr_dsc_read_test,
+ .att_flags = BLE_ATT_F_READ,
+ }, {
+ 0, /* No more descriptors in this characteristic */
+ } }
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_WRITE_NO_RSP),
+ .access_cb = gatt_svr_write_no_rsp_test,
+ .flags = BLE_GATT_CHR_F_READ |
+ BLE_GATT_CHR_F_WRITE |
+ BLE_GATT_CHR_F_WRITE_NO_RSP,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_AUTHEN),
+ .access_cb = gatt_svr_read_write_auth_test,
+ .flags = BLE_GATT_CHR_F_READ_AUTHEN |
+ BLE_GATT_CHR_F_READ |
+ BLE_GATT_CHR_F_WRITE_AUTHEN |
+ BLE_GATT_CHR_F_WRITE |
+ BLE_GATT_CHR_F_WRITE_AUTHEN,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_RELIABLE_WRITE),
+ .access_cb = gatt_svr_rel_write_test,
+ .flags = BLE_GATT_CHR_F_WRITE |
+ BLE_GATT_CHR_F_RELIABLE_WRITE,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ENC),
+ .access_cb = gatt_svr_read_write_enc_test,
+ .flags = BLE_GATT_CHR_F_READ_ENC |
+ BLE_GATT_CHR_F_READ |
+ BLE_GATT_CHR_F_WRITE |
+ BLE_GATT_CHR_F_WRITE_ENC,
+ .min_key_size = 16,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE),
+ .access_cb = gatt_svr_read_write_long_test,
+ .flags = BLE_GATT_CHR_F_WRITE |
+ BLE_GATT_CHR_F_READ,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_ALT),
+ .access_cb = gatt_svr_read_write_long_test,
+ .flags = BLE_GATT_CHR_F_WRITE |
+ BLE_GATT_CHR_F_READ,
+ }, {
+ .uuid = PTS_UUID_DECLARE(PTS_CHR_NOTIFY),
+ .access_cb = gatt_svr_read_write_test,
+ .val_handle = &notify_handle,
+ .flags = BLE_GATT_CHR_F_NOTIFY |
+ BLE_GATT_CHR_F_INDICATE,
+ }, {
+ 0, /* No more characteristics in this service. */
+ } },
+ },
+
+ {
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = PTS_UUID_DECLARE_ALT(PTS_SVC),
+ .characteristics = (struct ble_gatt_chr_def[]) { {
+ .uuid = PTS_UUID_DECLARE_ALT(PTS_CHR_READ_WRITE),
+ .access_cb = gatt_svr_read_write_test,
+ .flags = BLE_GATT_CHR_F_WRITE |
+ BLE_GATT_CHR_F_READ,
+ }, {
+ 0, /* No more characteristics in this service */
+ } },
+ },
+
+ {
+ 0, /* No more services. */
+ },
+};
+
+static void attr_value_changed_ev(u16_t handle, struct os_mbuf *data)
+{
+ struct gatt_attr_value_changed_ev *ev;
+ struct os_mbuf *buf = os_msys_get(0, 0);
+
+ SYS_LOG_DBG("");
+
+ net_buf_simple_init(buf, 0);
+ ev = net_buf_simple_add(buf, sizeof(*ev));
+
+ ev->handle = sys_cpu_to_le16(handle);
+ ev->data_length = sys_cpu_to_le16(os_mbuf_len(data));
+ os_mbuf_appendfrom(buf, data, 0, os_mbuf_len(data));
+
+ tester_send_buf(BTP_SERVICE_ID_GATT, GATT_EV_ATTR_VALUE_CHANGED,
+ CONTROLLER_INDEX, buf);
+}
+
+static int
+gatt_svr_chr_write(uint16_t conn_handle, uint16_t attr_handle,
+ struct os_mbuf *om, uint16_t min_len, uint16_t max_len,
+ void *dst, uint16_t *len)
+{
+ uint16_t om_len;
+ int rc;
+
+ om_len = OS_MBUF_PKTLEN(om);
+ if (om_len < min_len || om_len > max_len) {
+ return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
+ }
+
+ rc = ble_hs_mbuf_to_flat(om, dst, max_len, len);
+ if (rc != 0) {
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+
+ attr_value_changed_ev(attr_handle, om);
+
+ return 0;
+}
+
+static uint16_t
+extract_uuid16_from_pts_uuid128(const ble_uuid_t *uuid)
+{
+ const uint8_t *u8ptr;
+ uint16_t uuid16;
+
+ u8ptr = BLE_UUID128(uuid)->value;
+ uuid16 = u8ptr[12];
+ uuid16 |= (uint16_t)u8ptr[13] << 8;
+ return uuid16;
+}
+
+static int
+gatt_svr_read_write_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ uint16_t uuid16;
+ int rc;
+
+ uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid);
+ assert(uuid16 != 0);
+
+ switch (uuid16) {
+ case PTS_CHR_READ_WRITE:
+ case PTS_CHR_READ_WRITE_ALT:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+ rc = gatt_svr_chr_write(conn_handle, attr_handle,
+ ctxt->om, 0,
+ sizeof gatt_svr_pts_static_short_val,
+ &gatt_svr_pts_static_short_val, NULL);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_short_val,
+ sizeof gatt_svr_pts_static_short_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+}
+
+static int
+gatt_svr_read_write_long_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ uint16_t uuid16;
+ int rc;
+
+ uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid);
+ assert(uuid16 != 0);
+
+ switch (uuid16) {
+ case PTS_LONG_CHR_READ_WRITE:
+ case PTS_LONG_CHR_READ_WRITE_ALT:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+ rc = gatt_svr_chr_write(conn_handle, attr_handle,
+ ctxt->om, 0,
+ sizeof gatt_svr_pts_static_long_val,
+ &gatt_svr_pts_static_long_val, NULL);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val,
+ sizeof gatt_svr_pts_static_long_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+}
+
+static int
+gatt_svr_read_write_auth_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ uint16_t uuid16;
+ int rc;
+
+ uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid);
+ assert(uuid16 != 0);
+
+ switch (uuid16) {
+ case PTS_CHR_READ_WRITE_AUTHEN:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+ rc = gatt_svr_chr_write(conn_handle, attr_handle,
+ ctxt->om, 0,
+ sizeof gatt_svr_pts_static_val,
+ &gatt_svr_pts_static_val, NULL);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val,
+ sizeof gatt_svr_pts_static_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+}
+
+static int
+gatt_svr_read_write_enc_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ uint16_t uuid16;
+ int rc;
+
+ uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid);
+ assert(uuid16 != 0);
+
+ switch (uuid16) {
+ case PTS_CHR_READ_WRITE_ENC:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val,
+ sizeof gatt_svr_pts_static_val);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+ rc = gatt_svr_chr_write(conn_handle, attr_handle,
+ ctxt->om, 0,
+ sizeof gatt_svr_pts_static_val,
+ &gatt_svr_pts_static_val, NULL);
+ return rc;
+ }
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+}
+
+static int
+gatt_svr_dsc_read_write_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ uint16_t uuid16;
+ int rc;
+
+ uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid);
+ assert(uuid16 != 0);
+
+ switch (uuid16) {
+ case PTS_DSC_READ_WRITE:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) {
+ rc = gatt_svr_chr_write(conn_handle, attr_handle,
+ ctxt->om, 0,
+ sizeof gatt_svr_pts_static_short_val,
+ &gatt_svr_pts_static_short_val, NULL);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_short_val,
+ sizeof gatt_svr_pts_static_short_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+}
+
+static int
+gatt_svr_dsc_read_write_long_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ uint16_t uuid16;
+ int rc;
+
+ uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid);
+ assert(uuid16 != 0);
+
+ switch (uuid16) {
+ case PTS_LONG_DSC_READ_WRITE:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) {
+ rc = gatt_svr_chr_write(conn_handle, attr_handle,
+ ctxt->om, 0,
+ sizeof gatt_svr_pts_static_long_val,
+ &gatt_svr_pts_static_long_val, NULL);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val,
+ sizeof gatt_svr_pts_static_long_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+}
+
+static int
+gatt_svr_dsc_read_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ uint16_t uuid16;
+ int rc;
+
+ uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid);
+ assert(uuid16 != 0);
+
+ switch (uuid16) {
+ case PTS_DSC_READ:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val,
+ sizeof gatt_svr_pts_static_long_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+}
+
+static int
+gatt_svr_write_no_rsp_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ uint16_t uuid16;
+ int rc;
+
+ uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid);
+ assert(uuid16 != 0);
+
+ switch (uuid16) {
+ case PTS_CHR_WRITE_NO_RSP:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+ rc = gatt_svr_chr_write(conn_handle, attr_handle,
+ ctxt->om, 0,
+ sizeof gatt_svr_pts_static_short_val,
+ &gatt_svr_pts_static_short_val, NULL);
+ return rc;
+ } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_short_val,
+ sizeof gatt_svr_pts_static_short_val);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+}
+
+static int
+gatt_svr_rel_write_test(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ uint16_t uuid16;
+ int rc;
+
+ uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid);
+ assert(uuid16 != 0);
+
+ switch (uuid16) {
+ case PTS_CHR_RELIABLE_WRITE:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+ rc = gatt_svr_chr_write(conn_handle, attr_handle,
+ ctxt->om, 0,
+ sizeof gatt_svr_pts_static_val,
+ &gatt_svr_pts_static_val, NULL);
+ return rc;
+ }
+
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+}
+
+static void start_server(u8_t *data, u16_t len)
+{
+ struct gatt_start_server_rp rp;
+
+ SYS_LOG_DBG("");
+
+ ble_gatts_show_local();
+
+ ble_svc_gatt_changed(0x0001, 0xffff);
+
+ rp.db_attr_off = 0;
+ rp.db_attr_cnt = 0;
+
+ tester_send(BTP_SERVICE_ID_GATT, GATT_START_SERVER, CONTROLLER_INDEX,
+ (u8_t *) &rp, sizeof(rp));
+}
+
+/* Convert UUID from BTP command to bt_uuid */
+static u8_t btp2bt_uuid(const u8_t *uuid, u8_t len,
+ ble_uuid_any_t *bt_uuid)
+{
+ u16_t le16;
+
+ switch (len) {
+ case 0x02: /* UUID 16 */
+ bt_uuid->u.type = BLE_UUID_TYPE_16;
+ memcpy(&le16, uuid, sizeof(le16));
+ BLE_UUID16(bt_uuid)->value = sys_le16_to_cpu(le16);
+ break;
+ case 0x10: /* UUID 128*/
+ bt_uuid->u.type = BLE_UUID_TYPE_128;
+ memcpy(BLE_UUID128(bt_uuid)->value, uuid, 16);
+ break;
+ default:
+ return BTP_STATUS_FAILED;
+ }
+ return BTP_STATUS_SUCCESS;
+}
+
+/*
+ * gatt_buf - cache used by a gatt client (to cache data read/discovered)
+ * and gatt server (to store attribute user_data).
+ * It is not intended to be used by client and server at the same time.
+ */
+static struct {
+ u16_t len;
+ u8_t buf[MAX_BUFFER_SIZE];
+} gatt_buf;
+
+static void *gatt_buf_add(const void *data, size_t len)
+{
+ void *ptr = gatt_buf.buf + gatt_buf.len;
+
+ if ((len + gatt_buf.len) > MAX_BUFFER_SIZE) {
+ return NULL;
+ }
+
+ if (data) {
+ memcpy(ptr, data, len);
+ } else {
+ (void)memset(ptr, 0, len);
+ }
+
+ gatt_buf.len += len;
+
+ SYS_LOG_DBG("%d/%d used", gatt_buf.len, MAX_BUFFER_SIZE);
+
+ return ptr;
+}
+
+static void *gatt_buf_reserve(size_t len)
+{
+ return gatt_buf_add(NULL, len);
+}
+
+static void gatt_buf_clear(void)
+{
+ (void)memset(&gatt_buf, 0, sizeof(gatt_buf));
+}
+
+static void discover_destroy(void)
+{
+ gatt_buf_clear();
+}
+
+static void read_destroy()
+{
+ gatt_buf_clear();
+}
+
+static int read_cb(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ struct ble_gatt_attr *attr,
+ void *arg)
+{
+ struct gatt_read_rp *rp = (void *) gatt_buf.buf;
+ u8_t btp_opcode = (uint8_t) (int) arg;
+
+ SYS_LOG_DBG("status=%d", error->status);
+
+ if (error->status != 0 && error->status != BLE_HS_EDONE) {
+ rp->att_response = (uint8_t) BLE_HS_ATT_ERR(error->status);
+ tester_send(BTP_SERVICE_ID_GATT, btp_opcode,
+ CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
+ read_destroy();
+ return 0;
+ }
+
+ if (!gatt_buf_add(attr->om->om_data, attr->om->om_len)) {
+ tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ read_destroy();
+ return 0;
+ }
+
+ rp->data_length += attr->om->om_len;
+ tester_send(BTP_SERVICE_ID_GATT, btp_opcode,
+ CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
+ read_destroy();
+
+ return 0;
+}
+
+static void read(u8_t *data, u16_t len)
+{
+ const struct gatt_read_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc conn;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ goto fail;
+ }
+
+ /* Clear buffer */
+ read_destroy();
+
+ if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) {
+ goto fail;
+ }
+
+ if (ble_gattc_read(conn.conn_handle, sys_le16_to_cpu(cmd->handle),
+ read_cb, (void *)GATT_READ)) {
+ read_destroy();
+ goto fail;
+ }
+
+ return;
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static int read_long_cb(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ struct ble_gatt_attr *attr,
+ void *arg)
+{
+ struct gatt_read_rp *rp = (void *) gatt_buf.buf;
+ u8_t btp_opcode = (uint8_t) (int) arg;
+
+ SYS_LOG_DBG("status=%d", error->status);
+
+ if (error->status != 0 && error->status != BLE_HS_EDONE) {
+ rp->att_response = (uint8_t) BLE_HS_ATT_ERR(error->status);
+ tester_send(BTP_SERVICE_ID_GATT, btp_opcode,
+ CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
+ read_destroy();
+ return 0;
+ }
+
+ if (error->status == BLE_HS_EDONE) {
+ tester_send(BTP_SERVICE_ID_GATT, btp_opcode,
+ CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
+ read_destroy();
+ return 0;
+ }
+
+ if (gatt_buf_add(attr->om->om_data, attr->om->om_len) == NULL) {
+ tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ read_destroy();
+ return BLE_HS_ENOMEM;
+ }
+
+ rp->data_length += attr->om->om_len;
+
+ return 0;
+}
+
+static void read_long(u8_t *data, u16_t len)
+{
+ const struct gatt_read_long_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc conn;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ goto fail;
+ }
+
+ /* Clear buffer */
+ read_destroy();
+
+ if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) {
+ goto fail;
+ }
+
+ if (ble_gattc_read_long(conn.conn_handle,
+ sys_le16_to_cpu(cmd->handle),
+ sys_le16_to_cpu(cmd->offset),
+ read_long_cb, (void *)GATT_READ_LONG)) {
+ read_destroy();
+ goto fail;
+ }
+
+ return;
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ_LONG, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static void read_multiple(u8_t *data, u16_t len)
+{
+ const struct gatt_read_multiple_cmd *cmd = (void *) data;
+ u16_t handles[cmd->handles_count];
+ struct ble_gap_conn_desc conn;
+ int rc, i;
+
+ SYS_LOG_DBG("");
+
+ for (i = 0; i < ARRAY_SIZE(handles); i++) {
+ handles[i] = sys_le16_to_cpu(cmd->handles[i]);
+ }
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ goto fail;
+ }
+
+ /* Clear buffer */
+ read_destroy();
+
+ if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) {
+ goto fail;
+ }
+
+ if (ble_gattc_read_mult(conn.conn_handle, handles,
+ cmd->handles_count, read_cb,
+ (void *)GATT_READ_MULTIPLE)) {
+ read_destroy();
+ goto fail;
+ }
+
+ return;
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ_MULTIPLE, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static void write_without_rsp(u8_t *data, u16_t len, u8_t op, bool sign)
+{
+ const struct gatt_write_without_rsp_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc conn;
+ u8_t status = BTP_STATUS_SUCCESS;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ status = BTP_STATUS_FAILED;
+ goto rsp;
+ }
+
+ if (ble_gattc_write_no_rsp_flat(conn.conn_handle,
+ sys_le16_to_cpu(cmd->handle), cmd->data,
+ sys_le16_to_cpu(cmd->data_length))) {
+ status = BTP_STATUS_FAILED;
+ }
+
+rsp:
+ tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, status);
+}
+
+static int write_rsp(uint16_t conn_handle, const struct ble_gatt_error *error,
+ struct ble_gatt_attr *attr,
+ void *arg)
+{
+ uint8_t err = (uint8_t) error->status;
+ u8_t btp_opcode = (uint8_t) (int) arg;
+
+ SYS_LOG_DBG("");
+
+ tester_send(BTP_SERVICE_ID_GATT, btp_opcode,
+ CONTROLLER_INDEX, &err, sizeof(err));
+ return 0;
+}
+
+static void write(u8_t *data, u16_t len)
+{
+ const struct gatt_write_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc conn;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ goto fail;
+ }
+
+ if (ble_gattc_write_flat(conn.conn_handle, sys_le16_to_cpu(cmd->handle),
+ cmd->data, sys_le16_to_cpu(cmd->data_length),
+ write_rsp, (void *) GATT_WRITE)) {
+ goto fail;
+ }
+
+ return;
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_WRITE, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static void write_long(u8_t *data, u16_t len)
+{
+ const struct gatt_write_long_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc conn;
+ struct os_mbuf *om = NULL;
+ int rc = 0;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ goto fail;
+ }
+
+ om = ble_hs_mbuf_from_flat(cmd->data, sys_le16_to_cpu(cmd->data_length));
+ if (!om) {
+ SYS_LOG_ERR("Insufficient resources");
+ goto fail;
+ }
+
+ rc = ble_gattc_write_long(conn.conn_handle,
+ sys_le16_to_cpu(cmd->handle),
+ sys_le16_to_cpu(cmd->offset),
+ om, write_rsp,
+ (void *) GATT_WRITE_LONG);
+ if (!rc) {
+ return;
+ }
+
+fail:
+ SYS_LOG_ERR("Failed to send Write Long request, rc=%d", rc);
+ os_mbuf_free_chain(om);
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_WRITE_LONG, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static int reliable_write_rsp(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ struct ble_gatt_attr *attrs,
+ uint8_t num_attrs,
+ void *arg)
+{
+ uint8_t err = (uint8_t) error->status;
+
+ SYS_LOG_DBG("Reliable write status %d", err);
+
+ tester_send(BTP_SERVICE_ID_GATT, GATT_RELIABLE_WRITE,
+ CONTROLLER_INDEX, &err, sizeof(err));
+ return 0;
+}
+
+static void reliable_write(u8_t *data, u16_t len)
+{
+ const struct gatt_reliable_write_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc conn;
+ struct ble_gatt_attr attr;
+ struct os_mbuf *om = NULL;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ goto fail;
+ }
+
+ om = ble_hs_mbuf_from_flat(cmd->data, sys_le16_to_cpu(cmd->data_length));
+ /* This is required, because Nimble checks if
+ * the data is longer than offset
+ */
+ if (os_mbuf_extend(om, sys_le16_to_cpu(cmd->offset) + 1) == NULL) {
+ goto fail;
+ }
+
+ attr.handle = sys_le16_to_cpu(cmd->handle);
+ attr.offset = sys_le16_to_cpu(cmd->offset);
+ attr.om = om;
+
+ if (ble_gattc_write_reliable(conn.conn_handle, &attr, 1,
+ reliable_write_rsp, NULL)) {
+ goto fail;
+ }
+
+ return;
+
+fail:
+ os_mbuf_free_chain(om);
+
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_WRITE_LONG, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static struct bt_gatt_subscribe_params {
+ u16_t ccc_handle;
+ u16_t value;
+ u16_t value_handle;
+} subscribe_params;
+
+static void read_uuid(u8_t *data, u16_t len)
+{
+ const struct gatt_read_uuid_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc conn;
+ ble_uuid_any_t uuid;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ goto fail;
+ }
+
+ if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid)) {
+ goto fail;
+ }
+
+ /* Clear buffer */
+ read_destroy();
+
+ if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) {
+ goto fail;
+ }
+
+ if (ble_gattc_read_by_uuid(conn.conn_handle,
+ sys_le16_to_cpu(cmd->start_handle),
+ sys_le16_to_cpu(cmd->end_handle), &uuid.u,
+ read_long_cb, (void *)GATT_READ_UUID)) {
+ read_destroy();
+ goto fail;
+ }
+
+ return;
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static int disc_prim_uuid_cb(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ const struct ble_gatt_svc *gatt_svc, void *arg)
+{
+ struct gatt_disc_prim_uuid_rp *rp = (void *) gatt_buf.buf;
+ struct gatt_service *service;
+ const ble_uuid_any_t *uuid;
+ u8_t uuid_length;
+ u8_t opcode = (u8_t) (int) arg;
+
+ SYS_LOG_DBG("");
+
+ if (error->status != 0 && error->status != BLE_HS_EDONE) {
+ tester_rsp(BTP_SERVICE_ID_GATT, opcode,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ discover_destroy();
+ return 0;
+ }
+
+ if (error->status == BLE_HS_EDONE) {
+ tester_send(BTP_SERVICE_ID_GATT, opcode,
+ CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
+ discover_destroy();
+ return 0;
+ }
+
+ uuid = &gatt_svc->uuid;
+ uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16);
+
+ service = gatt_buf_reserve(sizeof(*service) + uuid_length);
+ if (!service) {
+ tester_rsp(BTP_SERVICE_ID_GATT, opcode,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ discover_destroy();
+ return BLE_HS_ENOMEM;
+ }
+
+ service->start_handle = sys_cpu_to_le16(gatt_svc->start_handle);
+ service->end_handle = sys_cpu_to_le16(gatt_svc->end_handle);
+ service->uuid_length = uuid_length;
+
+ if (uuid->u.type == BLE_UUID_TYPE_16) {
+ u16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value);
+ memcpy(service->uuid, &u16, uuid_length);
+ } else {
+ memcpy(service->uuid, BLE_UUID128(uuid)->value,
+ uuid_length);
+ }
+
+ rp->services_count++;
+
+ return 0;
+}
+
+static int disc_all_desc_cb(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ uint16_t chr_val_handle,
+ const struct ble_gatt_dsc *gatt_dsc,
+ void *arg)
+{
+ struct gatt_disc_all_desc_rp *rp = (void *) gatt_buf.buf;
+ struct gatt_descriptor *dsc;
+ const ble_uuid_any_t *uuid;
+ u8_t uuid_length;
+
+ SYS_LOG_DBG("");
+
+ if (error->status != 0 && error->status != BLE_HS_EDONE) {
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ discover_destroy();
+ return 0;
+ }
+
+ if (error->status == BLE_HS_EDONE) {
+ tester_send(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC,
+ CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
+ discover_destroy();
+ return 0;
+ }
+
+ uuid = &gatt_dsc->uuid;
+ uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16);
+
+ dsc = gatt_buf_reserve(sizeof(*dsc) + uuid_length);
+ if (!dsc) {
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ discover_destroy();
+ return BLE_HS_ENOMEM;
+ }
+
+ dsc->descriptor_handle = sys_cpu_to_le16(gatt_dsc->handle);
+ dsc->uuid_length = uuid_length;
+
+ if (uuid->u.type == BLE_UUID_TYPE_16) {
+ u16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value);
+ memcpy(dsc->uuid, &u16, uuid_length);
+ } else {
+ memcpy(dsc->uuid, BLE_UUID128(uuid)->value, uuid_length);
+ }
+
+ rp->descriptors_count++;
+
+ return 0;
+}
+
+static void disc_all_prim_svcs(u8_t *data, u16_t len)
+{
+ struct ble_gap_conn_desc conn;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ goto fail;
+ }
+
+ if (!gatt_buf_reserve(sizeof(struct gatt_disc_all_prim_svcs_rp))) {
+ goto fail;
+ }
+
+ if (ble_gattc_disc_all_svcs(conn.conn_handle, disc_prim_uuid_cb,
+ (void *) GATT_DISC_ALL_PRIM_SVCS)) {
+ discover_destroy();
+ goto fail;
+ }
+
+ return;
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_PRIM_SVCS,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+}
+
+static void disc_all_desc(u8_t *data, u16_t len)
+{
+ const struct gatt_disc_all_desc_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc conn;
+ uint16_t start_handle, end_handle;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ goto fail;
+ }
+
+ if (!gatt_buf_reserve(sizeof(struct gatt_disc_all_desc_rp))) {
+ goto fail;
+ }
+
+ start_handle = sys_le16_to_cpu(cmd->start_handle) - 1;
+ end_handle = sys_le16_to_cpu(cmd->end_handle);
+
+ rc = ble_gattc_disc_all_dscs(conn.conn_handle, start_handle, end_handle,
+ disc_all_desc_cb, NULL);
+
+ SYS_LOG_DBG("rc=%d", rc);
+
+ if (rc) {
+ discover_destroy();
+ goto fail;
+ }
+
+ return;
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static int find_included_cb(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ const struct ble_gatt_svc *gatt_svc, void *arg)
+{
+ struct gatt_find_included_rp *rp = (void *) gatt_buf.buf;
+ struct gatt_included *included;
+ const ble_uuid_any_t *uuid;
+ int service_handle = (int) arg;
+ u8_t uuid_length;
+
+ SYS_LOG_DBG("");
+
+ if (error->status != 0 && error->status != BLE_HS_EDONE) {
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ discover_destroy();
+ return 0;
+ }
+
+ if (error->status == BLE_HS_EDONE) {
+ tester_send(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED,
+ CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
+ discover_destroy();
+ return 0;
+ }
+
+ uuid = &gatt_svc->uuid;
+ uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16);
+
+
+ included = gatt_buf_reserve(sizeof(*included) + uuid_length);
+ if (!included) {
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ discover_destroy();
+ return BLE_HS_ENOMEM;
+ }
+
+ /* FIXME */
+ included->included_handle = sys_cpu_to_le16(service_handle + 1 +
+ rp->services_count);
+ included->service.start_handle = sys_cpu_to_le16(gatt_svc->start_handle);
+ included->service.end_handle = sys_cpu_to_le16(gatt_svc->end_handle);
+ included->service.uuid_length = uuid_length;
+
+ if (uuid->u.type == BLE_UUID_TYPE_16) {
+ u16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value);
+ memcpy(included->service.uuid, &u16, uuid_length);
+ } else {
+ memcpy(included->service.uuid, BLE_UUID128(uuid)->value,
+ uuid_length);
+ }
+
+ rp->services_count++;
+
+ return 0;
+}
+
+static int disc_chrc_cb(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ const struct ble_gatt_chr *gatt_chr, void *arg)
+{
+ struct gatt_disc_chrc_rp *rp = (void *) gatt_buf.buf;
+ struct gatt_characteristic *chrc;
+ const ble_uuid_any_t *uuid;
+ u8_t btp_opcode = (uint8_t) (int) arg;
+ u8_t uuid_length;
+
+ SYS_LOG_DBG("");
+
+ if (error->status != 0 && error->status != BLE_HS_EDONE) {
+ tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ discover_destroy();
+ return 0;
+ }
+
+ if (error->status == BLE_HS_EDONE) {
+ tester_send(BTP_SERVICE_ID_GATT, btp_opcode,
+ CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
+ discover_destroy();
+ return 0;
+ }
+
+ uuid = &gatt_chr->uuid;
+ uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16);
+
+ chrc = gatt_buf_reserve(sizeof(*chrc) + uuid_length);
+ if (!chrc) {
+ tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ discover_destroy();
+ return BLE_HS_ENOMEM;
+ }
+
+ chrc->characteristic_handle = sys_cpu_to_le16(gatt_chr->def_handle);
+ chrc->properties = gatt_chr->properties;
+ chrc->value_handle = sys_cpu_to_le16(gatt_chr->val_handle);
+ chrc->uuid_length = uuid_length;
+
+ if (uuid->u.type == BLE_UUID_TYPE_16) {
+ u16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value);
+ memcpy(chrc->uuid, &u16, uuid_length);
+ } else {
+ memcpy(chrc->uuid, BLE_UUID128(uuid)->value,
+ uuid_length);
+ }
+
+ rp->characteristics_count++;
+
+ return 0;
+}
+
+static void disc_chrc_uuid(u8_t *data, u16_t len)
+{
+ const struct gatt_disc_chrc_uuid_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc conn;
+ uint16_t start_handle, end_handle;
+ ble_uuid_any_t uuid;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ goto fail;
+ }
+
+ if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid)) {
+ goto fail;
+ }
+
+ if (!gatt_buf_reserve(sizeof(struct gatt_disc_chrc_rp))) {
+ goto fail;
+ }
+
+ start_handle = sys_le16_to_cpu(cmd->start_handle);
+ end_handle = sys_le16_to_cpu(cmd->end_handle);
+
+ if (ble_gattc_disc_chrs_by_uuid(conn.conn_handle, start_handle,
+ end_handle, &uuid.u, disc_chrc_cb,
+ (void *)GATT_DISC_CHRC_UUID)) {
+ discover_destroy();
+ goto fail;
+ }
+
+ return;
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_CHRC_UUID, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static void disc_prim_uuid(u8_t *data, u16_t len)
+{
+ const struct gatt_disc_prim_uuid_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc conn;
+ ble_uuid_any_t uuid;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ goto fail;
+ }
+
+ if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid)) {
+ goto fail;
+ }
+
+ if (!gatt_buf_reserve(sizeof(struct gatt_disc_prim_uuid_rp))) {
+ goto fail;
+ }
+
+ if (ble_gattc_disc_svc_by_uuid(conn.conn_handle,
+ &uuid.u, disc_prim_uuid_cb,
+ (void *) GATT_DISC_PRIM_UUID)) {
+ discover_destroy();
+ goto fail;
+ }
+
+ return;
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_PRIM_UUID, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static void disc_all_chrc(u8_t *data, u16_t len)
+{
+ const struct gatt_disc_all_chrc_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc conn;
+ uint16_t start_handle, end_handle;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ SYS_LOG_DBG("Conn find failed");
+ goto fail;
+ }
+
+ if (!gatt_buf_reserve(sizeof(struct gatt_disc_chrc_rp))) {
+ SYS_LOG_DBG("Buf reserve failed");
+ goto fail;
+ }
+
+ start_handle = sys_le16_to_cpu(cmd->start_handle);
+ end_handle = sys_le16_to_cpu(cmd->end_handle);
+
+ rc = ble_gattc_disc_all_chrs(conn.conn_handle, start_handle, end_handle,
+ disc_chrc_cb, (void *)GATT_DISC_ALL_CHRC);
+ if (rc) {
+ discover_destroy();
+ goto fail;
+ }
+
+ return;
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_CHRC, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static void find_included(u8_t *data, u16_t len)
+{
+ const struct gatt_find_included_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc conn;
+ uint16_t start_handle, end_handle;
+ int service_handle_arg;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ goto fail;
+ }
+
+ if (!gatt_buf_reserve(sizeof(struct gatt_find_included_rp))) {
+ goto fail;
+ }
+
+ start_handle = sys_le16_to_cpu(cmd->start_handle);
+ end_handle = sys_le16_to_cpu(cmd->end_handle);
+ service_handle_arg = start_handle;
+
+ if (ble_gattc_find_inc_svcs(conn.conn_handle, start_handle, end_handle,
+ find_included_cb,
+ (void *)service_handle_arg)) {
+ discover_destroy();
+ goto fail;
+ }
+
+ return;
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static int exchange_func(uint16_t conn_handle,
+ const struct ble_gatt_error *error,
+ uint16_t mtu, void *arg)
+{
+ SYS_LOG_DBG("");
+
+ if (error->status) {
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_EXCHANGE_MTU,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+
+ return 0;
+}
+
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_EXCHANGE_MTU, CONTROLLER_INDEX,
+ BTP_STATUS_SUCCESS);
+
+ return 0;
+}
+
+static void exchange_mtu(u8_t *data, u16_t len)
+{
+ struct ble_gap_conn_desc conn;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ goto fail;
+ }
+
+ if (ble_gattc_exchange_mtu(conn.conn_handle, exchange_func, NULL)) {
+ goto fail;
+ }
+
+ return;
+fail:
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_EXCHANGE_MTU,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+}
+
+static int enable_subscription(u16_t conn_handle, u16_t ccc_handle,
+ u16_t value)
+{
+ u8_t op;
+
+ SYS_LOG_DBG("");
+
+ op = (uint8_t) (value == 0x0001 ? GATT_CFG_NOTIFY : GATT_CFG_INDICATE);
+
+ if (ble_gattc_write_flat(conn_handle, ccc_handle,
+ &value, sizeof(value), NULL, NULL)) {
+ return -EINVAL;
+ }
+
+ subscribe_params.ccc_handle = value;
+
+ tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX,
+ BTP_STATUS_SUCCESS);
+ return 0;
+}
+
+static int disable_subscription(u16_t conn_handle, u16_t ccc_handle)
+{
+ u16_t value = 0x00;
+
+ SYS_LOG_DBG("");
+
+ /* Fail if CCC handle doesn't match */
+ if (ccc_handle != subscribe_params.ccc_handle) {
+ SYS_LOG_ERR("CCC handle doesn't match");
+ return -EINVAL;
+ }
+
+ if (ble_gattc_write_no_rsp_flat(conn_handle, ccc_handle,
+ &value, sizeof(value))) {
+ return -EINVAL;
+ }
+
+ subscribe_params.ccc_handle = 0;
+ return 0;
+}
+
+static void config_subscription(u8_t *data, u16_t len, u8_t op)
+{
+ const struct gatt_cfg_notify_cmd *cmd = (void *) data;
+ struct ble_gap_conn_desc conn;
+ u16_t ccc_handle = sys_le16_to_cpu(cmd->ccc_handle);
+ u8_t status;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+ if (rc) {
+ tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+ return;
+ }
+
+ if (cmd->enable) {
+ u16_t value;
+
+ if (op == GATT_CFG_NOTIFY) {
+ value = 0x0001;
+ } else {
+ value = 0x0002;
+ }
+
+ /* on success response will be sent from callback */
+ if (enable_subscription(conn.conn_handle,
+ ccc_handle, value) == 0) {
+ return;
+ }
+
+ status = BTP_STATUS_FAILED;
+ } else {
+ if (disable_subscription(conn.conn_handle, ccc_handle) < 0) {
+ status = BTP_STATUS_FAILED;
+ } else {
+ status = BTP_STATUS_SUCCESS;
+ }
+ }
+
+ SYS_LOG_DBG("Config subscription (op %u) status %u", op, status);
+
+ tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, status);
+}
+
+#define BTP_PERM_F_READ 0x01
+#define BTP_PERM_F_WRITE 0x02
+#define BTP_PERM_F_READ_ENC 0x04
+#define BTP_PERM_F_WRITE_ENC 0x08
+#define BTP_PERM_F_READ_AUTHEN 0x10
+#define BTP_PERM_F_WRITE_AUTHEN 0x20
+#define BTP_PERM_F_READ_AUTHOR 0x40
+#define BTP_PERM_F_WRITE_AUTHOR 0x80
+
+static int flags_hs2btp_map[] = {
+ BTP_PERM_F_READ,
+ BTP_PERM_F_WRITE,
+ BTP_PERM_F_READ_ENC,
+ BTP_PERM_F_READ_AUTHEN,
+ BTP_PERM_F_READ_AUTHOR,
+ BTP_PERM_F_WRITE_ENC,
+ BTP_PERM_F_WRITE_AUTHEN,
+ BTP_PERM_F_WRITE_AUTHOR,
+};
+
+static u8_t flags_hs2btp(u8_t flags)
+{
+ int i;
+ u8_t ret = 0;
+
+ for (i = 0; i < 8; ++i) {
+ if (flags & BIT(i)) {
+ ret |= flags_hs2btp_map[i];
+ }
+ }
+
+ return ret;
+}
+
+static void get_attrs(u8_t *data, u16_t len)
+{
+ const struct gatt_get_attributes_cmd *cmd = (void *) data;
+ struct gatt_get_attributes_rp *rp;
+ struct gatt_attr *gatt_attr;
+ struct os_mbuf *buf = os_msys_get(0, 0);
+ u16_t start_handle, end_handle;
+ struct ble_att_svr_entry *entry = NULL;
+ ble_uuid_any_t uuid;
+ ble_uuid_t *uuid_ptr = NULL;
+ u8_t count = 0;
+ char str[BLE_UUID_STR_LEN];
+
+ SYS_LOG_DBG("");
+
+ memset(str, 0, sizeof(str));
+ memset(&uuid, 0, sizeof(uuid));
+ start_handle = sys_le16_to_cpu(cmd->start_handle);
+ end_handle = sys_le16_to_cpu(cmd->end_handle);
+
+ if (cmd->type_length) {
+ if (btp2bt_uuid(cmd->type, cmd->type_length, &uuid)) {
+ goto fail;
+ }
+
+ ble_uuid_to_str(&uuid.u, str);
+ SYS_LOG_DBG("start 0x%04x end 0x%04x, uuid %s", start_handle,
+ end_handle, str);
+
+ uuid_ptr = &uuid.u;
+ } else {
+ SYS_LOG_DBG("start 0x%04x end 0x%04x", start_handle, end_handle);
+ }
+
+ net_buf_simple_init(buf, 0);
+ rp = net_buf_simple_add(buf, sizeof(*rp));
+
+ entry = ble_att_svr_find_by_uuid(entry, uuid_ptr, end_handle);
+ while (entry) {
+
+ if (entry->ha_handle_id < start_handle) {
+ entry = ble_att_svr_find_by_uuid(entry,
+ uuid_ptr, end_handle);
+ continue;
+ }
+
+ gatt_attr = net_buf_simple_add(buf, sizeof(*gatt_attr));
+ gatt_attr->handle = sys_cpu_to_le16(entry->ha_handle_id);
+ gatt_attr->permission = flags_hs2btp(entry->ha_flags);
+
+ if (entry->ha_uuid->type == BLE_UUID_TYPE_16) {
+ gatt_attr->type_length = 2;
+ net_buf_simple_add_le16(buf,
+ BLE_UUID16(entry->ha_uuid)->value);
+ } else {
+ gatt_attr->type_length = 16;
+ net_buf_simple_add_mem(buf,
+ BLE_UUID128(entry->ha_uuid)->value,
+ gatt_attr->type_length);
+ }
+
+ count++;
+
+ entry = ble_att_svr_find_by_uuid(entry, uuid_ptr, end_handle);
+ }
+
+ rp->attrs_count = count;
+
+ tester_send_buf(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTES,
+ CONTROLLER_INDEX, buf);
+
+ goto free;
+fail:
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTES, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+free:
+ os_mbuf_free_chain(buf);
+}
+
+static void get_attr_val(u8_t *data, u16_t len)
+{
+ const struct gatt_get_attribute_value_cmd *cmd = (void *) data;
+ struct gatt_get_attribute_value_rp *rp;
+ struct ble_gap_conn_desc conn;
+ struct os_mbuf *buf = os_msys_get(0, 0);
+ u16_t handle = sys_cpu_to_le16(cmd->handle);
+ uint8_t out_att_err;
+ int conn_status;
+
+ conn_status = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn);
+
+ if (conn_status) {
+ net_buf_simple_init(buf, 0);
+ rp = net_buf_simple_add(buf, sizeof(*rp));
+
+ ble_att_svr_read_handle(BLE_HS_CONN_HANDLE_NONE,
+ handle, 0, buf,
+ &out_att_err);
+
+ rp->att_response = out_att_err;
+ rp->value_length = os_mbuf_len(buf) - sizeof(*rp);
+
+ tester_send_buf(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTE_VALUE,
+ CONTROLLER_INDEX, buf);
+
+ goto free;
+ } else {
+ net_buf_simple_init(buf, 0);
+ rp = net_buf_simple_add(buf, sizeof(*rp));
+
+ ble_att_svr_read_handle(conn.conn_handle,
+ handle, 0, buf,
+ &out_att_err);
+
+ rp->att_response = out_att_err;
+ rp->value_length = os_mbuf_len(buf) - sizeof(*rp);
+
+ tester_send_buf(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTE_VALUE,
+ CONTROLLER_INDEX, buf);
+
+ goto free;
+ }
+
+free:
+ os_mbuf_free_chain(buf);
+}
+
+static void change_database(u8_t *data, u16_t len)
+{
+ const struct gatt_change_database *cmd = (void *) data;
+
+ SYS_LOG_DBG("")
+
+ ble_gatts_show_local();
+
+ ble_svc_gatt_changed(cmd->start_handle, cmd->end_handle);
+
+ tester_rsp(BTP_SERVICE_ID_GATT, GATT_CHANGE_DATABASE, CONTROLLER_INDEX,
+ BTP_STATUS_SUCCESS);
+
+ return;
+}
+
+static void supported_commands(u8_t *data, u16_t len)
+{
+ u8_t cmds[4];
+ struct gatt_read_supported_commands_rp *rp = (void *) cmds;
+
+ SYS_LOG_DBG("");
+
+ memset(cmds, 0, sizeof(cmds));
+
+ tester_set_bit(cmds, GATT_READ_SUPPORTED_COMMANDS);
+ tester_set_bit(cmds, GATT_START_SERVER);
+ tester_set_bit(cmds, GATT_EXCHANGE_MTU);
+ tester_set_bit(cmds, GATT_DISC_ALL_PRIM_SVCS);
+ tester_set_bit(cmds, GATT_DISC_PRIM_UUID);
+ tester_set_bit(cmds, GATT_FIND_INCLUDED);
+ tester_set_bit(cmds, GATT_DISC_ALL_CHRC);
+ tester_set_bit(cmds, GATT_DISC_CHRC_UUID);
+ tester_set_bit(cmds, GATT_DISC_ALL_DESC);
+ tester_set_bit(cmds, GATT_READ);
+ tester_set_bit(cmds, GATT_READ_LONG);
+ tester_set_bit(cmds, GATT_READ_MULTIPLE);
+ tester_set_bit(cmds, GATT_WRITE_WITHOUT_RSP);
+#if 0
+ tester_set_bit(cmds, GATT_SIGNED_WRITE_WITHOUT_RSP);
+#endif
+ tester_set_bit(cmds, GATT_WRITE);
+ tester_set_bit(cmds, GATT_WRITE_LONG);
+ tester_set_bit(cmds, GATT_CFG_NOTIFY);
+ tester_set_bit(cmds, GATT_CFG_INDICATE);
+ tester_set_bit(cmds, GATT_GET_ATTRIBUTES);
+ tester_set_bit(cmds, GATT_GET_ATTRIBUTE_VALUE);
+ tester_set_bit(cmds, GATT_CHANGE_DATABASE);
+
+ tester_send(BTP_SERVICE_ID_GATT, GATT_READ_SUPPORTED_COMMANDS,
+ CONTROLLER_INDEX, (u8_t *) rp, sizeof(cmds));
+}
+
+enum attr_type {
+ BLE_GATT_ATTR_SVC = 0,
+ BLE_GATT_ATTR_CHR,
+ BLE_GATT_ATTR_DSC,
+};
+
+void tester_handle_gatt(u8_t opcode, u8_t index, u8_t *data,
+ u16_t len)
+{
+ switch (opcode) {
+ case GATT_READ_SUPPORTED_COMMANDS:
+ supported_commands(data, len);
+ return;
+ case GATT_START_SERVER:
+ start_server(data, len);
+ return;
+ case GATT_EXCHANGE_MTU:
+ exchange_mtu(data, len);
+ return;
+ case GATT_DISC_ALL_PRIM_SVCS:
+ disc_all_prim_svcs(data, len);
+ return;
+ case GATT_DISC_PRIM_UUID:
+ disc_prim_uuid(data, len);
+ return;
+ case GATT_FIND_INCLUDED:
+ find_included(data, len);
+ return;
+ case GATT_DISC_ALL_CHRC:
+ disc_all_chrc(data, len);
+ return;
+ case GATT_DISC_CHRC_UUID:
+ disc_chrc_uuid(data, len);
+ return;
+ case GATT_DISC_ALL_DESC:
+ disc_all_desc(data, len);
+ return;
+ case GATT_CHANGE_DATABASE:
+ change_database(data, len);
+ return;
+ case GATT_READ:
+ read(data, len);
+ return;
+ case GATT_READ_UUID:
+ read_uuid(data, len);
+ return;
+ case GATT_READ_LONG:
+ read_long(data, len);
+ return;
+ case GATT_READ_MULTIPLE:
+ read_multiple(data, len);
+ return;
+ case GATT_WRITE_WITHOUT_RSP:
+ write_without_rsp(data, len, opcode, false);
+ return;
+#if 0
+ case GATT_SIGNED_WRITE_WITHOUT_RSP:
+ write_without_rsp(data, len, opcode, true);
+ return;
+#endif
+ case GATT_WRITE:
+ write(data, len);
+ return;
+ case GATT_WRITE_LONG:
+ write_long(data, len);
+ return;
+ case GATT_RELIABLE_WRITE:
+ reliable_write(data, len);
+ return;
+ case GATT_CFG_NOTIFY:
+ case GATT_CFG_INDICATE:
+ config_subscription(data, len, opcode);
+ return;
+ case GATT_GET_ATTRIBUTES:
+ get_attrs(data, len);
+ return;
+ case GATT_GET_ATTRIBUTE_VALUE:
+ get_attr_val(data, len);
+ return;
+ default:
+ tester_rsp(BTP_SERVICE_ID_GATT, opcode, index,
+ BTP_STATUS_UNKNOWN_CMD);
+ return;
+ }
+}
+
+int tester_gatt_notify_rx_ev(u16_t conn_handle, u16_t attr_handle,
+ u8_t indication, struct os_mbuf *om)
+{
+ struct gatt_notification_ev *ev;
+ struct ble_gap_conn_desc conn;
+ struct os_mbuf *buf = os_msys_get(0, 0);
+ const ble_addr_t *addr;
+
+ SYS_LOG_DBG("");
+
+ if (!subscribe_params.ccc_handle) {
+ goto fail;
+ }
+
+ if (ble_gap_conn_find(conn_handle, &conn)) {
+ goto fail;
+ }
+
+ net_buf_simple_init(buf, 0);
+ ev = net_buf_simple_add(buf, sizeof(*ev));
+
+ addr = &conn.peer_ota_addr;
+
+ ev->address_type = addr->type;
+ memcpy(ev->address, addr->val, sizeof(ev->address));
+ ev->type = (u8_t) (indication ? 0x02 : 0x01);
+ ev->handle = sys_cpu_to_le16(attr_handle);
+ ev->data_length = sys_cpu_to_le16(os_mbuf_len(om));
+ os_mbuf_appendfrom(buf, om, 0, os_mbuf_len(om));
+
+ tester_send_buf(BTP_SERVICE_ID_GATT, GATT_EV_NOTIFICATION,
+ CONTROLLER_INDEX, buf);
+
+fail:
+ os_mbuf_free_chain(buf);
+ return 0;
+}
+
+void notify_test_stop(void)
+{
+ os_callout_stop(&notify_tx_timer);
+}
+
+void notify_test_reset(void)
+{
+ int rc;
+
+ rc = os_callout_reset(&notify_tx_timer, OS_TICKS_PER_SEC);
+ assert(rc == 0);
+}
+
+void notify_test(struct os_event *ev)
+{
+ static uint8_t ntf[1];
+ struct os_mbuf *om;
+ int rc;
+
+ if (!notify_state && !indicate_state) {
+ notify_test_stop();
+ notify_value = 90;
+ return;
+ }
+
+ ntf[0] = notify_value;
+
+ notify_value++;
+ if (notify_value == 160) {
+ notify_value = 90;
+ }
+
+ om = ble_hs_mbuf_from_flat(ntf, sizeof(ntf));
+
+ if (notify_state) {
+ rc = ble_gattc_notify_custom(myconn_handle, notify_handle, om);
+ assert(rc == 0);
+ }
+
+ if (indicate_state) {
+ rc = ble_gattc_indicate_custom(myconn_handle, notify_handle, om);
+ assert(rc == 0);
+ }
+}
+
+int tester_gatt_subscribe_ev(u16_t conn_handle, u16_t attr_handle, u8_t reason,
+ u8_t prev_notify, u8_t cur_notify,
+ u8_t prev_indicate, u8_t cur_indicate)
+{
+ SYS_LOG_DBG("");
+ myconn_handle = conn_handle;
+
+ if (cur_notify == 0 && cur_indicate == 0) {
+ SYS_LOG_INF("Unsubscribed");
+ memset(&subscribe_params, 0, sizeof(subscribe_params));
+ return 0;
+ }
+
+ if (cur_notify) {
+ SYS_LOG_INF("Subscribed to notifications");
+ if (attr_handle == notify_handle) {
+ notify_state = cur_notify;
+ }
+ }
+
+ if (cur_indicate) {
+ SYS_LOG_INF("Subscribed to indications");
+ if (attr_handle == notify_handle) {
+ indicate_state = cur_indicate;
+ }
+ }
+
+
+ if (notify_state || indicate_state) {
+ notify_test_reset();
+ } else {
+ notify_test_stop();
+ }
+
+ return 0;
+}
+
+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:
+ MODLOG_DFLT(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:
+ MODLOG_DFLT(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:
+ MODLOG_DFLT(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_inc_svcs);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = ble_gatts_add_svcs(gatt_svr_inc_svcs);
+ if (rc != 0) {
+ return 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;
+}
+
+u8_t tester_init_gatt(void)
+{
+ os_callout_init(&notify_tx_timer, os_eventq_dflt_get(),
+ notify_test, NULL);
+
+ return BTP_STATUS_SUCCESS;
+}
+
+u8_t tester_unregister_gatt(void)
+{
+ return BTP_STATUS_SUCCESS;
+}
diff --git a/src/libs/mynewt-nimble/apps/bttester/src/glue.c b/src/libs/mynewt-nimble/apps/bttester/src/glue.c
new file mode 100644
index 00000000..6cd7643c
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/src/glue.c
@@ -0,0 +1,129 @@
+/*
+ * 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 "syscfg/syscfg.h"
+
+#if !MYNEWT_VAL(BLE_MESH)
+#include <assert.h>
+#include <string.h>
+#include "os/os.h"
+#include "os/os_mbuf.h"
+#include "glue.h"
+
+
+#define ASSERT_NOT_CHAIN(om) assert(SLIST_NEXT(om, om_next) == NULL)
+
+const char *bt_hex(const void *buf, size_t len)
+{
+ static const char hex[] = "0123456789abcdef";
+ static char hexbufs[4][137];
+ static u8_t curbuf;
+ const u8_t *b = buf;
+ char *str;
+ int i;
+
+ str = hexbufs[curbuf++];
+ curbuf %= ARRAY_SIZE(hexbufs);
+
+ len = min(len, (sizeof(hexbufs[0]) - 1) / 2);
+
+ for (i = 0; i < len; i++) {
+ str[i * 2] = hex[b[i] >> 4];
+ str[i * 2 + 1] = hex[b[i] & 0xf];
+ }
+
+ str[i * 2] = '\0';
+
+ return str;
+}
+
+struct os_mbuf * NET_BUF_SIMPLE(uint16_t size)
+{
+ struct os_mbuf *buf;
+
+ buf = os_msys_get(size, 0);
+ assert(buf);
+
+ return buf;
+}
+
+/* This is by purpose */
+void net_buf_simple_init(struct os_mbuf *buf,
+ size_t reserve_head)
+{
+ /* This is called in Zephyr after init.
+ * Note in Mynewt case we don't care abour reserved head*/
+ buf->om_data = &buf->om_databuf[buf->om_pkthdr_len] + reserve_head;
+ buf->om_len = 0;
+}
+
+void
+net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val)
+{
+ val = htole16(val);
+ os_mbuf_append(om, &val, sizeof(val));
+ ASSERT_NOT_CHAIN(om);
+}
+
+void
+net_buf_simple_add_be16(struct os_mbuf *om, uint16_t val)
+{
+ val = htobe16(val);
+ os_mbuf_append(om, &val, sizeof(val));
+ ASSERT_NOT_CHAIN(om);
+}
+
+void
+net_buf_simple_add_be32(struct os_mbuf *om, uint32_t val)
+{
+ val = htobe32(val);
+ os_mbuf_append(om, &val, sizeof(val));
+ ASSERT_NOT_CHAIN(om);
+}
+
+void
+net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val)
+{
+ os_mbuf_append(om, &val, 1);
+ ASSERT_NOT_CHAIN(om);
+}
+
+void*
+net_buf_simple_add(struct os_mbuf *om, uint8_t len)
+{
+ void * tmp;
+
+ tmp = os_mbuf_extend(om, len);
+ ASSERT_NOT_CHAIN(om);
+
+ return tmp;
+}
+
+uint8_t *
+net_buf_simple_push(struct os_mbuf *om, uint8_t len)
+{
+ uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len];
+
+ assert(headroom >= len);
+ om->om_data -= len;
+ om->om_len += len;
+
+ return om->om_data;
+}
+#endif
diff --git a/src/libs/mynewt-nimble/apps/bttester/src/glue.h b/src/libs/mynewt-nimble/apps/bttester/src/glue.h
new file mode 100644
index 00000000..e563331e
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/src/glue.h
@@ -0,0 +1,63 @@
+/*
+ * 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 __GLUE_H__
+#define __GLUE_H__
+
+#include "os/endian.h"
+
+#define u8_t uint8_t
+#define s8_t int8_t
+#define u16_t uint16_t
+#define u32_t uint32_t
+#define s32_t int32_t
+
+#ifndef BIT
+#define BIT(n) (1UL << (n))
+#endif
+
+#define __packed __attribute__((__packed__))
+
+#define sys_le16_to_cpu le16toh
+
+struct bt_data {
+ u8_t type;
+ u8_t data_len;
+ const u8_t *data;
+};
+
+#define BT_DATA(_type, _data, _data_len) \
+ { \
+ .type = (_type), \
+ .data_len = (_data_len), \
+ .data = (const u8_t *)(_data), \
+ }
+
+struct os_mbuf * NET_BUF_SIMPLE(uint16_t size);
+void net_buf_simple_init(struct os_mbuf *buf, size_t reserve_head);
+void net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val);
+void net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val);
+void *net_buf_simple_add(struct os_mbuf *om, uint8_t len);
+uint8_t *net_buf_simple_push(struct os_mbuf *om, uint8_t len);
+
+#define net_buf_simple_add_mem(a,b,c) os_mbuf_append(a,b,c)
+
+const char *bt_hex(const void *buf, size_t len);
+
+#endif /* __GLUE_H__ */
diff --git a/src/libs/mynewt-nimble/apps/bttester/src/l2cap.c b/src/libs/mynewt-nimble/apps/bttester/src/l2cap.c
new file mode 100644
index 00000000..45b904a1
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/src/l2cap.c
@@ -0,0 +1,477 @@
+/*
+ * 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.
+ */
+
+/* l2cap.c - Bluetooth L2CAP Tester */
+
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+
+#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
+
+#include "console/console.h"
+#include "host/ble_gap.h"
+#include "host/ble_l2cap.h"
+
+#include "bttester.h"
+
+#define CONTROLLER_INDEX 0
+#define CHANNELS MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
+#define TESTER_COC_MTU (230)
+#define TESTER_COC_BUF_COUNT (3 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM))
+
+static os_membuf_t tester_sdu_coc_mem[
+ OS_MEMPOOL_SIZE(TESTER_COC_BUF_COUNT, TESTER_COC_MTU)
+];
+
+struct os_mbuf_pool sdu_os_mbuf_pool;
+static struct os_mempool sdu_coc_mbuf_mempool;
+
+static struct channel {
+ u8_t chan_id; /* Internal number that identifies L2CAP channel. */
+ u8_t state;
+ struct ble_l2cap_chan *chan;
+} channels[CHANNELS];
+
+static u8_t recv_cb_buf[TESTER_COC_MTU + sizeof(struct l2cap_data_received_ev)];
+
+struct channel *find_channel(struct ble_l2cap_chan *chan) {
+ int i;
+
+ for (i = 0; i < CHANNELS; ++i) {
+ if (channels[i].chan == chan) {
+ return &channels[i];
+ }
+ }
+
+ return NULL;
+}
+
+static void
+tester_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu)
+{
+ SYS_LOG_DBG("LE CoC SDU received, chan: 0x%08lx, data len %d",
+ (uint32_t) chan, OS_MBUF_PKTLEN(sdu));
+
+ os_mbuf_free_chain(sdu);
+ sdu = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
+ assert(sdu != NULL);
+
+ ble_l2cap_recv_ready(chan, sdu);
+}
+
+static void recv_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
+ struct os_mbuf *buf, void *arg)
+{
+ struct l2cap_data_received_ev *ev = (void *) recv_cb_buf;
+ struct channel *channel = arg;
+
+ ev->chan_id = channel->chan_id;
+ ev->data_length = buf->om_len;
+ memcpy(ev->data, buf->om_data, buf->om_len);
+
+ tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DATA_RECEIVED,
+ CONTROLLER_INDEX, recv_cb_buf, sizeof(*ev) + buf->om_len);
+
+ tester_l2cap_coc_recv(chan, buf);
+}
+
+static void unstalled_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
+ int status, void *arg)
+{
+ if (status) {
+ tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA,
+ CONTROLLER_INDEX, BTP_STATUS_FAILED);
+ } else {
+ tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA,
+ CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
+ }
+}
+
+static struct channel *get_free_channel(void)
+{
+ u8_t i;
+ struct channel *chan;
+
+ for (i = 0; i < CHANNELS; i++) {
+ if (channels[i].state) {
+ continue;
+ }
+
+ chan = &channels[i];
+ chan->chan_id = i;
+
+ return chan;
+ }
+
+ return NULL;
+}
+
+static void connected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
+ void *arg)
+{
+ struct l2cap_connected_ev ev;
+ struct ble_gap_conn_desc desc;
+ struct channel *channel;
+
+ channel = get_free_channel();
+ if (!channel) {
+ assert(0);
+ }
+
+ channel->chan = chan;
+ channel->state = 0;
+
+ ev.chan_id = channel->chan_id;
+ channel->state = 1;
+ channel->chan = chan;
+ /* TODO: ev.psm */
+
+ if (!ble_gap_conn_find(conn_handle, &desc)) {
+ ev.address_type = desc.peer_ota_addr.type;
+ memcpy(ev.address, desc.peer_ota_addr.val,
+ sizeof(ev.address));
+ }
+
+ tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_CONNECTED, CONTROLLER_INDEX,
+ (u8_t *) &ev, sizeof(ev));
+}
+
+static void disconnected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
+ void *arg)
+{
+ struct l2cap_disconnected_ev ev;
+ struct ble_gap_conn_desc desc;
+ struct channel *channel;
+
+ memset(&ev, 0, sizeof(struct l2cap_disconnected_ev));
+
+ channel = find_channel(chan);
+ if (channel != NULL) {
+ channel->state = 0;
+ channel->chan = chan;
+
+ ev.chan_id = channel->chan_id;
+ /* TODO: ev.result */
+ /* TODO: ev.psm */
+ }
+
+ if (!ble_gap_conn_find(conn_handle, &desc)) {
+ ev.address_type = desc.peer_ota_addr.type;
+ memcpy(ev.address, desc.peer_ota_addr.val,
+ sizeof(ev.address));
+ }
+
+ tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DISCONNECTED,
+ CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
+}
+
+static int accept_cb(uint16_t conn_handle, uint16_t peer_mtu,
+ struct ble_l2cap_chan *chan)
+{
+ struct os_mbuf *sdu_rx;
+
+ SYS_LOG_DBG("LE CoC accepting, chan: 0x%08lx, peer_mtu %d",
+ (uint32_t) chan, peer_mtu);
+
+ sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
+ if (!sdu_rx) {
+ return BLE_HS_ENOMEM;
+ }
+
+ ble_l2cap_recv_ready(chan, sdu_rx);
+
+ return 0;
+}
+
+static int
+tester_l2cap_event(struct ble_l2cap_event *event, void *arg)
+{
+ struct ble_l2cap_chan_info chan_info;
+
+ switch (event->type) {
+ case BLE_L2CAP_EVENT_COC_CONNECTED:
+ if (event->connect.status) {
+ console_printf("LE COC error: %d\n", event->connect.status);
+ disconnected_cb(event->connect.conn_handle,
+ event->connect.chan, arg);
+ return 0;
+ }
+
+ ble_l2cap_get_chan_info(event->connect.chan, &chan_info);
+
+ console_printf("LE COC connected, conn: %d, chan: 0x%08lx, scid: 0x%04x, "
+ "dcid: 0x%04x, our_mtu: 0x%04x, peer_mtu: 0x%04x\n",
+ event->connect.conn_handle,
+ (uint32_t) event->connect.chan,
+ chan_info.scid,
+ chan_info.dcid,
+ chan_info.our_l2cap_mtu,
+ chan_info.peer_l2cap_mtu);
+
+ connected_cb(event->connect.conn_handle,
+ event->connect.chan, arg);
+
+ return 0;
+ case BLE_L2CAP_EVENT_COC_DISCONNECTED:
+ console_printf("LE CoC disconnected, chan: 0x%08lx\n",
+ (uint32_t) event->disconnect.chan);
+
+ disconnected_cb(event->disconnect.conn_handle,
+ event->disconnect.chan, arg);
+ return 0;
+ case BLE_L2CAP_EVENT_COC_ACCEPT:
+ console_printf("LE CoC accept, chan: 0x%08lx, handle: %u, sdu_size: %u\n",
+ (uint32_t) event->accept.chan,
+ event->accept.conn_handle,
+ event->accept.peer_sdu_size);
+
+ return accept_cb(event->accept.conn_handle,
+ event->accept.peer_sdu_size,
+ event->accept.chan);
+
+ case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
+ console_printf("LE CoC data received, chan: 0x%08lx, handle: %u, sdu_len: %u\n",
+ (uint32_t) event->receive.chan,
+ event->receive.conn_handle,
+ event->receive.sdu_rx->om_len);
+ recv_cb(event->receive.conn_handle, event->receive.chan,
+ event->receive.sdu_rx, arg);
+ return 0;
+ case BLE_L2CAP_EVENT_COC_TX_UNSTALLED:
+ console_printf("LE CoC tx unstalled, chan: 0x%08lx, handle: %u, status: %d\n",
+ (uint32_t) event->tx_unstalled.chan,
+ event->tx_unstalled.conn_handle,
+ event->tx_unstalled.status);
+ unstalled_cb(event->tx_unstalled.conn_handle,
+ event->tx_unstalled.chan,
+ event->tx_unstalled.status, arg);
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static void connect(u8_t *data, u16_t len)
+{
+ const struct l2cap_connect_cmd *cmd = (void *) data;
+ struct l2cap_connect_rp rp;
+ struct ble_gap_conn_desc desc;
+ struct channel *chan;
+ struct os_mbuf *sdu_rx;
+ ble_addr_t *addr = (void *) data;
+ int rc;
+
+ SYS_LOG_DBG("connect: type: %d addr: %s", addr->type, bt_hex(addr->val, 6));
+
+ rc = ble_gap_conn_find_by_addr(addr, &desc);
+ if (rc) {
+ SYS_LOG_ERR("GAP conn find failed");
+ goto fail;
+ }
+
+ chan = get_free_channel();
+ if (!chan) {
+ SYS_LOG_ERR("No free channels");
+ goto fail;
+ }
+
+ sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
+ if (sdu_rx == NULL) {
+ SYS_LOG_ERR("Failed to alloc buf");
+ goto fail;
+ }
+
+ rc = ble_l2cap_connect(desc.conn_handle, htole16(cmd->psm),
+ TESTER_COC_MTU, sdu_rx,
+ tester_l2cap_event, chan);
+ if (rc) {
+ SYS_LOG_ERR("L2CAP connect failed\n");
+ goto fail;
+ }
+
+ rp.chan_id = chan->chan_id;
+
+ tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_CONNECT, CONTROLLER_INDEX,
+ (u8_t *) &rp, sizeof(rp));
+
+ return;
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_CONNECT, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static void disconnect(u8_t *data, u16_t len)
+{
+ const struct l2cap_disconnect_cmd *cmd = (void *) data;
+ struct channel *chan;
+ u8_t status;
+ int err;
+
+ SYS_LOG_DBG("");
+
+ chan = &channels[cmd->chan_id];
+
+ err = ble_l2cap_disconnect(chan->chan);
+ if (err) {
+ status = BTP_STATUS_FAILED;
+ goto rsp;
+ }
+
+ status = BTP_STATUS_SUCCESS;
+
+rsp:
+ tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_DISCONNECT, CONTROLLER_INDEX,
+ status);
+}
+
+static void send_data(u8_t *data, u16_t len)
+{
+ const struct l2cap_send_data_cmd *cmd = (void *) data;
+ struct channel *chan = &channels[cmd->chan_id];
+ struct os_mbuf *sdu_tx = NULL;
+ int rc;
+ u16_t data_len = sys_le16_to_cpu(cmd->data_len);
+
+ SYS_LOG_DBG("cmd->chan_id=%d", cmd->chan_id);
+
+ /* FIXME: For now, fail if data length exceeds buffer length */
+ if (data_len > TESTER_COC_MTU) {
+ SYS_LOG_ERR("Data length exceeds buffer length");
+ goto fail;
+ }
+
+ sdu_tx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
+ if (sdu_tx == NULL) {
+ SYS_LOG_ERR("No memory in the test sdu pool\n");
+ goto fail;
+ }
+
+ os_mbuf_append(sdu_tx, cmd->data, data_len);
+
+ /* ble_l2cap_send takes ownership of the sdu */
+ rc = ble_l2cap_send(chan->chan, sdu_tx);
+ if (rc == 0 || rc == BLE_HS_ESTALLED) {
+ tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, CONTROLLER_INDEX,
+ BTP_STATUS_SUCCESS);
+ return;
+ }
+
+ SYS_LOG_ERR("Unable to send data: %d", rc);
+ os_mbuf_free_chain(sdu_tx);
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static void listen(u8_t *data, u16_t len)
+{
+ const struct l2cap_listen_cmd *cmd = (void *) data;
+ int rc;
+
+ SYS_LOG_DBG("");
+
+ /* TODO: Handle cmd->transport flag */
+ rc = ble_l2cap_create_server(cmd->psm, TESTER_COC_MTU,
+ tester_l2cap_event, NULL);
+ if (rc) {
+ goto fail;
+ }
+
+ tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_LISTEN, CONTROLLER_INDEX,
+ BTP_STATUS_SUCCESS);
+ return;
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_LISTEN, CONTROLLER_INDEX,
+ BTP_STATUS_FAILED);
+}
+
+static void supported_commands(u8_t *data, u16_t len)
+{
+ u8_t cmds[1];
+ struct l2cap_read_supported_commands_rp *rp = (void *) cmds;
+
+ memset(cmds, 0, sizeof(cmds));
+
+ tester_set_bit(cmds, L2CAP_READ_SUPPORTED_COMMANDS);
+ tester_set_bit(cmds, L2CAP_CONNECT);
+ tester_set_bit(cmds, L2CAP_DISCONNECT);
+ tester_set_bit(cmds, L2CAP_LISTEN);
+ tester_set_bit(cmds, L2CAP_SEND_DATA);
+
+ tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_READ_SUPPORTED_COMMANDS,
+ CONTROLLER_INDEX, (u8_t *) rp, sizeof(cmds));
+}
+
+void tester_handle_l2cap(u8_t opcode, u8_t index, u8_t *data,
+ u16_t len)
+{
+ switch (opcode) {
+ case L2CAP_READ_SUPPORTED_COMMANDS:
+ supported_commands(data, len);
+ return;
+ case L2CAP_CONNECT:
+ connect(data, len);
+ return;
+ case L2CAP_DISCONNECT:
+ disconnect(data, len);
+ return;
+ case L2CAP_SEND_DATA:
+ send_data(data, len);
+ return;
+ case L2CAP_LISTEN:
+ listen(data, len);
+ return;
+ default:
+ tester_rsp(BTP_SERVICE_ID_L2CAP, opcode, index,
+ BTP_STATUS_UNKNOWN_CMD);
+ return;
+ }
+}
+
+u8_t tester_init_l2cap(void)
+{
+ int rc;
+
+ /* For testing we want to support all the available channels */
+ rc = os_mempool_init(&sdu_coc_mbuf_mempool, TESTER_COC_BUF_COUNT,
+ TESTER_COC_MTU, tester_sdu_coc_mem,
+ "tester_coc_sdu_pool");
+ assert(rc == 0);
+
+ rc = os_mbuf_pool_init(&sdu_os_mbuf_pool, &sdu_coc_mbuf_mempool,
+ TESTER_COC_MTU, TESTER_COC_BUF_COUNT);
+ assert(rc == 0);
+
+ return BTP_STATUS_SUCCESS;
+}
+
+u8_t tester_unregister_l2cap(void)
+{
+ return BTP_STATUS_SUCCESS;
+}
+
+#endif
diff --git a/src/libs/mynewt-nimble/apps/bttester/src/main.c b/src/libs/mynewt-nimble/apps/bttester/src/main.c
new file mode 100644
index 00000000..ea130805
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/src/main.c
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+/* main.c - Application main entry point */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "sysinit/sysinit.h"
+
+#include "modlog/modlog.h"
+#include "host/ble_uuid.h"
+#include "host/ble_hs.h"
+
+#include "bttester.h"
+
+static void on_reset(int reason)
+{
+ MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
+}
+
+static void on_sync(void)
+{
+ MODLOG_DFLT(INFO, "Bluetooth initialized\n");
+
+ tester_init();
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+
+#ifdef ARCH_sim
+ mcu_sim_parse_args(argc, argv);
+#endif
+
+ /* Initialize OS */
+ sysinit();
+
+ /* Initialize the NimBLE host configuration. */
+ ble_hs_cfg.reset_cb = on_reset;
+ ble_hs_cfg.sync_cb = on_sync;
+ ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb,
+ ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
+
+ rc = gatt_svr_init();
+ assert(rc == 0);
+
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/bttester/src/mesh.c b/src/libs/mynewt-nimble/apps/bttester/src/mesh.c
new file mode 100644
index 00000000..e18a2a4e
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/src/mesh.c
@@ -0,0 +1,970 @@
+/*
+ * 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.
+ */
+
+/* mesh.c - Bluetooth Mesh Tester */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+
+#if MYNEWT_VAL(BLE_MESH)
+
+#include <errno.h>
+
+#include "mesh/mesh.h"
+#include "mesh/glue.h"
+#include "mesh/testing.h"
+#include "console/console.h"
+
+#include "bttester.h"
+
+extern u8_t own_addr_type;
+
+#define CONTROLLER_INDEX 0
+#define CID_LOCAL 0xffff
+
+/* Health server data */
+#define CUR_FAULTS_MAX 4
+#define HEALTH_TEST_ID 0x00
+
+static u8_t cur_faults[CUR_FAULTS_MAX];
+static u8_t reg_faults[CUR_FAULTS_MAX * 2];
+
+/* Provision node data */
+static u8_t net_key[16];
+static u16_t net_key_idx;
+static u8_t flags;
+static u32_t iv_index;
+static u16_t addr;
+static u8_t dev_key[16];
+static u8_t input_size;
+
+/* Configured provisioning data */
+static u8_t dev_uuid[16];
+static u8_t static_auth[16];
+
+/* Vendor Model data */
+#define VND_MODEL_ID_1 0x1234
+
+/* Model send data */
+#define MODEL_BOUNDS_MAX 2
+
+static struct model_data {
+ struct bt_mesh_model *model;
+ u16_t addr;
+ u16_t appkey_idx;
+} model_bound[MODEL_BOUNDS_MAX];
+
+static struct {
+ u16_t local;
+ u16_t dst;
+ u16_t net_idx;
+} net = {
+ .local = BT_MESH_ADDR_UNASSIGNED,
+ .dst = BT_MESH_ADDR_UNASSIGNED,
+};
+
+static void supported_commands(u8_t *data, u16_t len)
+{
+ struct os_mbuf *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE);
+
+ net_buf_simple_init(buf, 0);
+
+ /* 1st octet */
+ memset(net_buf_simple_add(buf, 1), 0, 1);
+ tester_set_bit(buf->om_data, MESH_READ_SUPPORTED_COMMANDS);
+ tester_set_bit(buf->om_data, MESH_CONFIG_PROVISIONING);
+ tester_set_bit(buf->om_data, MESH_PROVISION_NODE);
+ tester_set_bit(buf->om_data, MESH_INIT);
+ tester_set_bit(buf->om_data, MESH_RESET);
+ tester_set_bit(buf->om_data, MESH_INPUT_NUMBER);
+ tester_set_bit(buf->om_data, MESH_INPUT_STRING);
+ /* 2nd octet */
+ tester_set_bit(buf->om_data, MESH_IVU_TEST_MODE);
+ tester_set_bit(buf->om_data, MESH_IVU_TOGGLE_STATE);
+ tester_set_bit(buf->om_data, MESH_NET_SEND);
+ tester_set_bit(buf->om_data, MESH_HEALTH_GENERATE_FAULTS);
+ tester_set_bit(buf->om_data, MESH_HEALTH_CLEAR_FAULTS);
+ tester_set_bit(buf->om_data, MESH_LPN);
+ tester_set_bit(buf->om_data, MESH_LPN_POLL);
+ tester_set_bit(buf->om_data, MESH_MODEL_SEND);
+ /* 3rd octet */
+ memset(net_buf_simple_add(buf, 1), 0, 1);
+#if MYNEWT_VAL(BLE_MESH_TESTING)
+ tester_set_bit(buf->om_data, MESH_LPN_SUBSCRIBE);
+ tester_set_bit(buf->om_data, MESH_LPN_UNSUBSCRIBE);
+ tester_set_bit(buf->om_data, MESH_RPL_CLEAR);
+#endif /* CONFIG_BT_TESTING */
+ tester_set_bit(buf->om_data, MESH_PROXY_IDENTITY);
+
+ tester_send_buf(BTP_SERVICE_ID_MESH, MESH_READ_SUPPORTED_COMMANDS,
+ CONTROLLER_INDEX, buf);
+}
+
+static struct bt_mesh_cfg_srv cfg_srv = {
+ .relay = BT_MESH_RELAY_ENABLED,
+ .beacon = BT_MESH_BEACON_ENABLED,
+#if MYNEWT_VAL(BLE_MESH_FRIEND)
+ .frnd = BT_MESH_FRIEND_ENABLED,
+#else
+ .frnd = BT_MESH_FRIEND_NOT_SUPPORTED,
+#endif
+#if MYNEWT_VAL(BLE_MESH_GATT_PROXY)
+ .gatt_proxy = BT_MESH_GATT_PROXY_ENABLED,
+#else
+ .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
+#endif
+ .default_ttl = 7,
+
+ /* 3 transmissions with 20ms interval */
+ .net_transmit = BT_MESH_TRANSMIT(2, 20),
+ .relay_retransmit = BT_MESH_TRANSMIT(2, 20),
+};
+
+static void get_faults(u8_t *faults, u8_t faults_size, u8_t *dst, u8_t *count)
+{
+ u8_t i, limit = *count;
+
+ for (i = 0, *count = 0; i < faults_size && *count < limit; i++) {
+ if (faults[i]) {
+ *dst++ = faults[i];
+ (*count)++;
+ }
+ }
+}
+
+static int fault_get_cur(struct bt_mesh_model *model, u8_t *test_id,
+ u16_t *company_id, u8_t *faults, u8_t *fault_count)
+{
+ SYS_LOG_DBG("");
+
+ *test_id = HEALTH_TEST_ID;
+ *company_id = CID_LOCAL;
+
+ get_faults(cur_faults, sizeof(cur_faults), faults, fault_count);
+
+ return 0;
+}
+
+static int fault_get_reg(struct bt_mesh_model *model, u16_t company_id,
+ u8_t *test_id, u8_t *faults, u8_t *fault_count)
+{
+ SYS_LOG_DBG("company_id 0x%04x", company_id);
+
+ if (company_id != CID_LOCAL) {
+ return -EINVAL;
+ }
+
+ *test_id = HEALTH_TEST_ID;
+
+ get_faults(reg_faults, sizeof(reg_faults), faults, fault_count);
+
+ return 0;
+}
+
+static int fault_clear(struct bt_mesh_model *model, uint16_t company_id)
+{
+ SYS_LOG_DBG("company_id 0x%04x", company_id);
+
+ if (company_id != CID_LOCAL) {
+ return -EINVAL;
+ }
+
+ memset(reg_faults, 0, sizeof(reg_faults));
+
+ return 0;
+}
+
+static int fault_test(struct bt_mesh_model *model, uint8_t test_id,
+ uint16_t company_id)
+{
+ SYS_LOG_DBG("test_id 0x%02x company_id 0x%04x", test_id, company_id);
+
+ if (company_id != CID_LOCAL || test_id != HEALTH_TEST_ID) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct bt_mesh_health_srv_cb health_srv_cb = {
+ .fault_get_cur = fault_get_cur,
+ .fault_get_reg = fault_get_reg,
+ .fault_clear = fault_clear,
+ .fault_test = fault_test,
+};
+
+static struct bt_mesh_health_srv health_srv = {
+ .cb = &health_srv_cb,
+};
+
+static struct bt_mesh_model_pub health_pub;
+
+static void
+health_pub_init(void)
+{
+ health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(CUR_FAULTS_MAX);
+}
+
+static struct bt_mesh_cfg_cli cfg_cli = {
+};
+
+void show_faults(u8_t test_id, u16_t cid, u8_t *faults, size_t fault_count)
+{
+ size_t i;
+
+ if (!fault_count) {
+ SYS_LOG_DBG("Health Test ID 0x%02x Company ID 0x%04x: "
+ "no faults", test_id, cid);
+ return;
+ }
+
+ SYS_LOG_DBG("Health Test ID 0x%02x Company ID 0x%04x Fault Count %zu: ",
+ test_id, cid, fault_count);
+
+ for (i = 0; i < fault_count; i++) {
+ SYS_LOG_DBG("0x%02x", faults[i]);
+ }
+}
+
+static void health_current_status(struct bt_mesh_health_cli *cli, u16_t addr,
+ u8_t test_id, u16_t cid, u8_t *faults,
+ size_t fault_count)
+{
+ SYS_LOG_DBG("Health Current Status from 0x%04x", addr);
+ show_faults(test_id, cid, faults, fault_count);
+}
+
+static struct bt_mesh_health_cli health_cli = {
+ .current_status = health_current_status,
+};
+
+static struct bt_mesh_model root_models[] = {
+ BT_MESH_MODEL_CFG_SRV(&cfg_srv),
+ BT_MESH_MODEL_CFG_CLI(&cfg_cli),
+ BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
+ BT_MESH_MODEL_HEALTH_CLI(&health_cli),
+};
+
+static struct bt_mesh_model vnd_models[] = {
+ BT_MESH_MODEL_VND(CID_LOCAL, VND_MODEL_ID_1, BT_MESH_MODEL_NO_OPS, NULL,
+ NULL),
+};
+
+static struct bt_mesh_elem elements[] = {
+ BT_MESH_ELEM(0, root_models, vnd_models),
+};
+
+static void link_open(bt_mesh_prov_bearer_t bearer)
+{
+ struct mesh_prov_link_open_ev ev;
+
+ SYS_LOG_DBG("bearer 0x%02x", bearer);
+
+ switch (bearer) {
+ case BT_MESH_PROV_ADV:
+ ev.bearer = MESH_PROV_BEARER_PB_ADV;
+ break;
+ case BT_MESH_PROV_GATT:
+ ev.bearer = MESH_PROV_BEARER_PB_GATT;
+ break;
+ default:
+ SYS_LOG_ERR("Invalid bearer");
+
+ return;
+ }
+
+ tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROV_LINK_OPEN,
+ CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
+}
+
+static void link_close(bt_mesh_prov_bearer_t bearer)
+{
+ struct mesh_prov_link_closed_ev ev;
+
+ SYS_LOG_DBG("bearer 0x%02x", bearer);
+
+ switch (bearer) {
+ case BT_MESH_PROV_ADV:
+ ev.bearer = MESH_PROV_BEARER_PB_ADV;
+ break;
+ case BT_MESH_PROV_GATT:
+ ev.bearer = MESH_PROV_BEARER_PB_GATT;
+ break;
+ default:
+ SYS_LOG_ERR("Invalid bearer");
+
+ return;
+ }
+
+ tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROV_LINK_CLOSED,
+ CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
+}
+
+static int output_number(bt_mesh_output_action_t action, u32_t number)
+{
+ struct mesh_out_number_action_ev ev;
+
+ SYS_LOG_DBG("action 0x%04x number 0x%08lx", action, number);
+
+ ev.action = sys_cpu_to_le16(action);
+ ev.number = sys_cpu_to_le32(number);
+
+ tester_send(BTP_SERVICE_ID_MESH, MESH_EV_OUT_NUMBER_ACTION,
+ CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
+
+ return 0;
+}
+
+static int output_string(const char *str)
+{
+ struct mesh_out_string_action_ev *ev;
+ struct os_mbuf *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE);
+
+ SYS_LOG_DBG("str %s", str);
+
+ net_buf_simple_init(buf, 0);
+
+ ev = net_buf_simple_add(buf, sizeof(*ev));
+ ev->string_len = strlen(str);
+
+ net_buf_simple_add_mem(buf, str, ev->string_len);
+
+ tester_send_buf(BTP_SERVICE_ID_MESH, MESH_EV_OUT_STRING_ACTION,
+ CONTROLLER_INDEX, buf);
+
+ os_mbuf_free_chain(buf);
+ return 0;
+}
+
+static int input(bt_mesh_input_action_t action, u8_t size)
+{
+ struct mesh_in_action_ev ev;
+
+ SYS_LOG_DBG("action 0x%04x number 0x%02x", action, size);
+
+ input_size = size;
+
+ ev.action = sys_cpu_to_le16(action);
+ ev.size = size;
+
+ tester_send(BTP_SERVICE_ID_MESH, MESH_EV_IN_ACTION, CONTROLLER_INDEX,
+ (u8_t *) &ev, sizeof(ev));
+
+ return 0;
+}
+
+static void prov_complete(u16_t net_idx, u16_t addr)
+{
+ SYS_LOG_DBG("net_idx 0x%04x addr 0x%04x", net_idx, addr);
+
+ net.net_idx = net_idx,
+ net.local = addr;
+ net.dst = addr;
+
+ tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROVISIONED, CONTROLLER_INDEX,
+ NULL, 0);
+}
+
+static void prov_reset(void)
+{
+ SYS_LOG_DBG("");
+
+ bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
+}
+
+static const struct bt_mesh_comp comp = {
+ .cid = CID_LOCAL,
+ .elem = elements,
+ .elem_count = ARRAY_SIZE(elements),
+};
+
+static struct bt_mesh_prov prov = {
+ .uuid = dev_uuid,
+ .static_val = static_auth,
+ .static_val_len = sizeof(static_auth),
+ .output_number = output_number,
+ .output_string = output_string,
+ .input = input,
+ .link_open = link_open,
+ .link_close = link_close,
+ .complete = prov_complete,
+ .reset = prov_reset,
+};
+
+static void config_prov(u8_t *data, u16_t len)
+{
+ const struct mesh_config_provisioning_cmd *cmd = (void *) data;
+
+ SYS_LOG_DBG("");
+
+ memcpy(dev_uuid, cmd->uuid, sizeof(dev_uuid));
+ memcpy(static_auth, cmd->static_auth, sizeof(static_auth));
+
+ prov.output_size = cmd->out_size;
+ prov.output_actions = sys_le16_to_cpu(cmd->out_actions);
+ prov.input_size = cmd->in_size;
+ prov.input_actions = sys_le16_to_cpu(cmd->in_actions);
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_CONFIG_PROVISIONING,
+ CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
+}
+
+static void provision_node(u8_t *data, u16_t len)
+{
+ const struct mesh_provision_node_cmd *cmd = (void *) data;
+
+ SYS_LOG_DBG("");
+
+ memcpy(dev_key, cmd->dev_key, sizeof(dev_key));
+ memcpy(net_key, cmd->net_key, sizeof(net_key));
+
+ addr = sys_le16_to_cpu(cmd->addr);
+ flags = cmd->flags;
+ iv_index = sys_le32_to_cpu(cmd->iv_index);
+ net_key_idx = sys_le16_to_cpu(cmd->net_key_idx);
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_PROVISION_NODE,
+ CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
+}
+
+static void init(u8_t *data, u16_t len)
+{
+ u8_t status = BTP_STATUS_SUCCESS;
+ int err;
+
+ SYS_LOG_DBG("");
+
+ err = bt_mesh_init(own_addr_type, &prov, &comp);
+ if (err) {
+ status = BTP_STATUS_FAILED;
+
+ goto rsp;
+ }
+
+ if (addr) {
+ err = bt_mesh_provision(net_key, net_key_idx, flags, iv_index,
+ addr, dev_key);
+ if (err) {
+ status = BTP_STATUS_FAILED;
+ }
+ } else {
+ err = bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
+ if (err) {
+ status = BTP_STATUS_FAILED;
+ }
+ }
+
+ /* Set device key for vendor model */
+ vnd_models[0].keys[0] = BT_MESH_KEY_DEV;
+
+rsp:
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_INIT, CONTROLLER_INDEX,
+ status);
+}
+
+static void reset(u8_t *data, u16_t len)
+{
+ SYS_LOG_DBG("");
+
+ bt_mesh_reset();
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_RESET, CONTROLLER_INDEX,
+ BTP_STATUS_SUCCESS);
+}
+
+static void input_number(u8_t *data, u16_t len)
+{
+ const struct mesh_input_number_cmd *cmd = (void *) data;
+ u8_t status = BTP_STATUS_SUCCESS;
+ u32_t number;
+ int err;
+
+ number = sys_le32_to_cpu(cmd->number);
+
+ SYS_LOG_DBG("number 0x%04lx", number);
+
+ err = bt_mesh_input_number(number);
+ if (err) {
+ status = BTP_STATUS_FAILED;
+ }
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_INPUT_NUMBER, CONTROLLER_INDEX,
+ status);
+}
+
+static void input_string(u8_t *data, u16_t len)
+{
+ const struct mesh_input_string_cmd *cmd = (void *) data;
+ u8_t status = BTP_STATUS_SUCCESS;
+ u8_t str_auth[16];
+ int err;
+
+ SYS_LOG_DBG("");
+
+ if (cmd->string_len > sizeof(str_auth)) {
+ SYS_LOG_ERR("Too long input (%u chars required)", input_size);
+ status = BTP_STATUS_FAILED;
+ goto rsp;
+ } else if (cmd->string_len < input_size) {
+ SYS_LOG_ERR("Too short input (%u chars required)", input_size);
+ status = BTP_STATUS_FAILED;
+ goto rsp;
+ }
+
+ strncpy((char *)str_auth, (char *)cmd->string, cmd->string_len);
+
+ err = bt_mesh_input_string((char *)str_auth);
+ if (err) {
+ status = BTP_STATUS_FAILED;
+ }
+
+rsp:
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_INPUT_STRING, CONTROLLER_INDEX,
+ status);
+}
+
+static void ivu_test_mode(u8_t *data, u16_t len)
+{
+ const struct mesh_ivu_test_mode_cmd *cmd = (void *) data;
+
+ SYS_LOG_DBG("enable 0x%02x", cmd->enable);
+
+ bt_mesh_iv_update_test(cmd->enable ? true : false);
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_IVU_TEST_MODE, CONTROLLER_INDEX,
+ BTP_STATUS_SUCCESS);
+}
+
+static void ivu_toggle_state(u8_t *data, u16_t len)
+{
+ bool result;
+
+ SYS_LOG_DBG("");
+
+ result = bt_mesh_iv_update();
+ if (!result) {
+ SYS_LOG_ERR("Failed to toggle the IV Update state");
+ }
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_IVU_TOGGLE_STATE, CONTROLLER_INDEX,
+ result ? BTP_STATUS_SUCCESS : BTP_STATUS_FAILED);
+}
+
+static void lpn(u8_t *data, u16_t len)
+{
+ struct mesh_lpn_set_cmd *cmd = (void *) data;
+ bool enable;
+ int err;
+
+ SYS_LOG_DBG("enable 0x%02x", cmd->enable);
+
+ enable = cmd->enable ? true : false;
+ err = bt_mesh_lpn_set(enable);
+ if (err) {
+ SYS_LOG_ERR("Failed to toggle LPN (err %d)", err);
+ }
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN, CONTROLLER_INDEX,
+ err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
+}
+
+static void lpn_poll(u8_t *data, u16_t len)
+{
+ int err;
+
+ SYS_LOG_DBG("");
+
+ err = bt_mesh_lpn_poll();
+ if (err) {
+ SYS_LOG_ERR("Failed to send poll msg (err %d)", err);
+ }
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_POLL, CONTROLLER_INDEX,
+ err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
+}
+
+static void net_send(u8_t *data, u16_t len)
+{
+ struct mesh_net_send_cmd *cmd = (void *) data;
+ struct os_mbuf *msg = NET_BUF_SIMPLE(UINT8_MAX);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net.net_idx,
+ .app_idx = BT_MESH_KEY_DEV,
+ .addr = sys_le16_to_cpu(cmd->dst),
+ .send_ttl = cmd->ttl,
+ };
+ int err;
+
+ SYS_LOG_DBG("ttl 0x%02x dst 0x%04x payload_len %d", ctx.send_ttl,
+ ctx.addr, cmd->payload_len);
+
+ net_buf_simple_add_mem(msg, cmd->payload, cmd->payload_len);
+
+ err = bt_mesh_model_send(&vnd_models[0], &ctx, msg, NULL, NULL);
+ if (err) {
+ SYS_LOG_ERR("Failed to send (err %d)", err);
+ }
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_NET_SEND, CONTROLLER_INDEX,
+ err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
+
+ os_mbuf_free_chain(msg);
+}
+
+static void health_generate_faults(u8_t *data, u16_t len)
+{
+ struct mesh_health_generate_faults_rp *rp;
+ struct os_mbuf *buf = NET_BUF_SIMPLE(sizeof(*rp) + sizeof(cur_faults) +
+ sizeof(reg_faults));
+ u8_t some_faults[] = { 0x01, 0x02, 0x03, 0xff, 0x06 };
+ u8_t cur_faults_count, reg_faults_count;
+
+ rp = net_buf_simple_add(buf, sizeof(*rp));
+
+ cur_faults_count = min(sizeof(cur_faults), sizeof(some_faults));
+ memcpy(cur_faults, some_faults, cur_faults_count);
+ net_buf_simple_add_mem(buf, cur_faults, cur_faults_count);
+ rp->cur_faults_count = cur_faults_count;
+
+ reg_faults_count = min(sizeof(reg_faults), sizeof(some_faults));
+ memcpy(reg_faults, some_faults, reg_faults_count);
+ net_buf_simple_add_mem(buf, reg_faults, reg_faults_count);
+ rp->reg_faults_count = reg_faults_count;
+
+ bt_mesh_fault_update(&elements[0]);
+
+ tester_send_buf(BTP_SERVICE_ID_MESH, MESH_HEALTH_GENERATE_FAULTS,
+ CONTROLLER_INDEX, buf);
+}
+
+static void health_clear_faults(u8_t *data, u16_t len)
+{
+ SYS_LOG_DBG("");
+
+ memset(cur_faults, 0, sizeof(cur_faults));
+ memset(reg_faults, 0, sizeof(reg_faults));
+
+ bt_mesh_fault_update(&elements[0]);
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_HEALTH_CLEAR_FAULTS,
+ CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
+}
+
+static void model_send(u8_t *data, u16_t len)
+{
+ struct mesh_model_send_cmd *cmd = (void *) data;
+ struct os_mbuf *msg = NET_BUF_SIMPLE(UINT8_MAX);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net.net_idx,
+ .app_idx = BT_MESH_KEY_DEV,
+ .addr = sys_le16_to_cpu(cmd->dst),
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct bt_mesh_model *model = NULL;
+ int err, i;
+ u16_t src = sys_le16_to_cpu(cmd->src);
+
+ /* Lookup source address */
+ for (i = 0; i < ARRAY_SIZE(model_bound); i++) {
+ if (bt_mesh_model_elem(model_bound[i].model)->addr == src) {
+ model = model_bound[i].model;
+ ctx.app_idx = model_bound[i].appkey_idx;
+
+ break;
+ }
+ }
+
+ if (!model) {
+ SYS_LOG_ERR("Model not found");
+ err = -EINVAL;
+
+ goto fail;
+ }
+
+ SYS_LOG_DBG("src 0x%04x dst 0x%04x model %p payload_len %d", src,
+ ctx.addr, model, cmd->payload_len);
+
+ net_buf_simple_add_mem(msg, cmd->payload, cmd->payload_len);
+
+ err = bt_mesh_model_send(model, &ctx, msg, NULL, NULL);
+ if (err) {
+ SYS_LOG_ERR("Failed to send (err %d)", err);
+ }
+
+fail:
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_MODEL_SEND, CONTROLLER_INDEX,
+ err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
+
+ os_mbuf_free_chain(msg);
+}
+
+#if MYNEWT_VAL(BLE_MESH_TESTING)
+static void lpn_subscribe(u8_t *data, u16_t len)
+{
+ struct mesh_lpn_subscribe_cmd *cmd = (void *) data;
+ u16_t address = sys_le16_to_cpu(cmd->address);
+ int err;
+
+ SYS_LOG_DBG("address 0x%04x", address);
+
+ err = bt_test_mesh_lpn_group_add(address);
+ if (err) {
+ SYS_LOG_ERR("Failed to subscribe (err %d)", err);
+ }
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_SUBSCRIBE, CONTROLLER_INDEX,
+ err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
+}
+
+static void lpn_unsubscribe(u8_t *data, u16_t len)
+{
+ struct mesh_lpn_unsubscribe_cmd *cmd = (void *) data;
+ u16_t address = sys_le16_to_cpu(cmd->address);
+ int err;
+
+ SYS_LOG_DBG("address 0x%04x", address);
+
+ err = bt_test_mesh_lpn_group_remove(&address, 1);
+ if (err) {
+ SYS_LOG_ERR("Failed to unsubscribe (err %d)", err);
+ }
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_UNSUBSCRIBE, CONTROLLER_INDEX,
+ err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
+}
+
+static void rpl_clear(u8_t *data, u16_t len)
+{
+ int err;
+
+ SYS_LOG_DBG("");
+
+ err = bt_test_mesh_rpl_clear();
+ if (err) {
+ SYS_LOG_ERR("Failed to clear RPL (err %d)", err);
+ }
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_RPL_CLEAR, CONTROLLER_INDEX,
+ err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
+}
+#endif /* MYNEWT_VAL(BLE_MESH_TESTING) */
+
+static void proxy_identity_enable(u8_t *data, u16_t len)
+{
+ int err;
+
+ SYS_LOG_DBG("");
+
+ err = bt_mesh_proxy_identity_enable();
+ if (err) {
+ SYS_LOG_ERR("Failed to enable proxy identity (err %d)", err);
+ }
+
+ tester_rsp(BTP_SERVICE_ID_MESH, MESH_PROXY_IDENTITY, CONTROLLER_INDEX,
+ err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
+}
+
+void tester_handle_mesh(u8_t opcode, u8_t index, u8_t *data, u16_t len)
+{
+ switch (opcode) {
+ case MESH_READ_SUPPORTED_COMMANDS:
+ supported_commands(data, len);
+ break;
+ case MESH_CONFIG_PROVISIONING:
+ config_prov(data, len);
+ break;
+ case MESH_PROVISION_NODE:
+ provision_node(data, len);
+ break;
+ case MESH_INIT:
+ init(data, len);
+ break;
+ case MESH_RESET:
+ reset(data, len);
+ break;
+ case MESH_INPUT_NUMBER:
+ input_number(data, len);
+ break;
+ case MESH_INPUT_STRING:
+ input_string(data, len);
+ break;
+ case MESH_IVU_TEST_MODE:
+ ivu_test_mode(data, len);
+ break;
+ case MESH_IVU_TOGGLE_STATE:
+ ivu_toggle_state(data, len);
+ break;
+ case MESH_LPN:
+ lpn(data, len);
+ break;
+ case MESH_LPN_POLL:
+ lpn_poll(data, len);
+ break;
+ case MESH_NET_SEND:
+ net_send(data, len);
+ break;
+ case MESH_HEALTH_GENERATE_FAULTS:
+ health_generate_faults(data, len);
+ break;
+ case MESH_HEALTH_CLEAR_FAULTS:
+ health_clear_faults(data, len);
+ break;
+ case MESH_MODEL_SEND:
+ model_send(data, len);
+ break;
+#if MYNEWT_VAL(BLE_MESH_TESTING)
+ case MESH_LPN_SUBSCRIBE:
+ lpn_subscribe(data, len);
+ break;
+ case MESH_LPN_UNSUBSCRIBE:
+ lpn_unsubscribe(data, len);
+ break;
+ case MESH_RPL_CLEAR:
+ rpl_clear(data, len);
+ break;
+#endif /* MYNEWT_VAL(BLE_MESH_TESTING) */
+ case MESH_PROXY_IDENTITY:
+ proxy_identity_enable(data, len);
+ break;
+ default:
+ tester_rsp(BTP_SERVICE_ID_MESH, opcode, index,
+ BTP_STATUS_UNKNOWN_CMD);
+ break;
+ }
+}
+
+void net_recv_ev(u8_t ttl, u8_t ctl, u16_t src, u16_t dst, const void *payload,
+ size_t payload_len)
+{
+ struct os_mbuf *buf = NET_BUF_SIMPLE(UINT8_MAX);
+ struct mesh_net_recv_ev *ev;
+
+ SYS_LOG_DBG("ttl 0x%02x ctl 0x%02x src 0x%04x dst 0x%04x "
+ "payload_len %d", ttl, ctl, src, dst, payload_len);
+
+ if (payload_len > net_buf_simple_tailroom(buf)) {
+ SYS_LOG_ERR("Payload size exceeds buffer size");
+
+ goto done;
+ }
+
+ ev = net_buf_simple_add(buf, sizeof(*ev));
+ ev->ttl = ttl;
+ ev->ctl = ctl;
+ ev->src = sys_cpu_to_le16(src);
+ ev->dst = sys_cpu_to_le16(dst);
+ ev->payload_len = payload_len;
+ net_buf_simple_add_mem(buf, payload, payload_len);
+
+ tester_send_buf(BTP_SERVICE_ID_MESH, MESH_EV_NET_RECV, CONTROLLER_INDEX,
+ buf);
+done:
+ os_mbuf_free_chain(buf);
+}
+
+static void model_bound_cb(u16_t addr, struct bt_mesh_model *model,
+ u16_t key_idx)
+{
+ int i;
+
+ SYS_LOG_DBG("remote addr 0x%04x key_idx 0x%04x model %p",
+ addr, key_idx, model);
+
+ for (i = 0; i < ARRAY_SIZE(model_bound); i++) {
+ if (!model_bound[i].model) {
+ model_bound[i].model = model;
+ model_bound[i].addr = addr;
+ model_bound[i].appkey_idx = key_idx;
+
+ return;
+ }
+ }
+
+ SYS_LOG_ERR("model_bound is full");
+}
+
+static void model_unbound_cb(u16_t addr, struct bt_mesh_model *model,
+ u16_t key_idx)
+{
+ int i;
+
+ SYS_LOG_DBG("remote addr 0x%04x key_idx 0x%04x model %p",
+ addr, key_idx, model);
+
+ for (i = 0; i < ARRAY_SIZE(model_bound); i++) {
+ if (model_bound[i].model == model) {
+ model_bound[i].model = NULL;
+ model_bound[i].addr = 0x0000;
+ model_bound[i].appkey_idx = BT_MESH_KEY_UNUSED;
+
+ return;
+ }
+ }
+
+ SYS_LOG_INF("model not found");
+}
+
+static void invalid_bearer_cb(u8_t opcode)
+{
+ struct mesh_invalid_bearer_ev ev = {
+ .opcode = opcode,
+ };
+
+ SYS_LOG_DBG("opcode 0x%02x", opcode);
+
+ tester_send(BTP_SERVICE_ID_MESH, MESH_EV_INVALID_BEARER,
+ CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
+}
+
+static void incomp_timer_exp_cb(void)
+{
+ tester_send(BTP_SERVICE_ID_MESH, MESH_EV_INCOMP_TIMER_EXP,
+ CONTROLLER_INDEX, NULL, 0);
+}
+
+static struct bt_test_cb bt_test_cb = {
+ .mesh_net_recv = net_recv_ev,
+ .mesh_model_bound = model_bound_cb,
+ .mesh_model_unbound = model_unbound_cb,
+ .mesh_prov_invalid_bearer = invalid_bearer_cb,
+ .mesh_trans_incomp_timer_exp = incomp_timer_exp_cb,
+};
+
+u8_t tester_init_mesh(void)
+{
+ health_pub_init();
+
+ if (IS_ENABLED(CONFIG_BT_TESTING)) {
+ bt_test_cb_register(&bt_test_cb);
+ }
+
+ return BTP_STATUS_SUCCESS;
+}
+
+u8_t tester_unregister_mesh(void)
+{
+ return BTP_STATUS_SUCCESS;
+}
+
+#endif /* MYNEWT_VAL(BLE_MESH) */
diff --git a/src/libs/mynewt-nimble/apps/bttester/src/rtt_pipe.c b/src/libs/mynewt-nimble/apps/bttester/src/rtt_pipe.c
new file mode 100644
index 00000000..379345a0
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/src/rtt_pipe.c
@@ -0,0 +1,136 @@
+/*
+ * 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 "syscfg/syscfg.h"
+
+#if MYNEWT_VAL(BTTESTER_PIPE_RTT)
+
+#include "os/mynewt.h"
+#include "console/console.h"
+#include "rtt/SEGGER_RTT.h"
+
+#include "bttester_pipe.h"
+
+static struct hal_timer rtt_timer;
+
+static bttester_pipe_recv_cb app_cb;
+
+static u8_t *recv_buf;
+static size_t recv_buf_len;
+static size_t recv_off;
+
+static uint8_t rtt_buf_up[MYNEWT_VAL(BTTESTER_RTT_BUFFER_SIZE_UP)];
+static uint8_t rtt_buf_down[MYNEWT_VAL(BTTESTER_RTT_BUFFER_SIZE_DOWN)];
+static int rtt_index_up, rtt_index_down;
+
+#define RTT_INPUT_POLL_INTERVAL_MIN 10 /* ms */
+#define RTT_INPUT_POLL_INTERVAL_STEP 10 /* ms */
+#define RTT_INPUT_POLL_INTERVAL_MAX 250 /* ms */
+
+static int rtt_pipe_get_char(unsigned int index)
+{
+ char c;
+ int r;
+
+ r = (int)SEGGER_RTT_Read(index, &c, 1u);
+ if (r == 1) {
+ r = (int)(unsigned char)c;
+ } else {
+ r = -1;
+ }
+
+ return r;
+}
+
+static void
+rtt_pipe_poll_func(void *arg)
+{
+ static uint32_t itvl_ms = RTT_INPUT_POLL_INTERVAL_MIN;
+ static int key = -1;
+ int avail = recv_buf_len - recv_off;
+
+ if (key < 0) {
+ key = rtt_pipe_get_char((unsigned int) rtt_index_down);
+ }
+
+ if (key < 0) {
+ itvl_ms += RTT_INPUT_POLL_INTERVAL_STEP;
+ itvl_ms = min(itvl_ms, RTT_INPUT_POLL_INTERVAL_MAX);
+ } else {
+ while (key >= 0 && avail > 0) {
+ recv_buf[recv_off] = (u8_t) key;
+ recv_off++;
+ avail = recv_buf_len - recv_off;
+ key = rtt_pipe_get_char((unsigned int) rtt_index_down);
+ }
+
+ /*
+ * Call application callback with received data. Application
+ * may provide new buffer or alter data offset.
+ */
+ recv_buf = app_cb(recv_buf, &recv_off);
+
+ itvl_ms = RTT_INPUT_POLL_INTERVAL_MIN;
+ }
+
+ os_cputime_timer_relative(&rtt_timer, itvl_ms * 1000);
+}
+
+int
+bttester_pipe_send(const u8_t *data, int len)
+{
+ SEGGER_RTT_Write((unsigned int) rtt_index_up, data, (unsigned int) len);
+ return 0;
+}
+
+void
+bttester_pipe_register(u8_t *buf, size_t len, bttester_pipe_recv_cb cb)
+{
+ recv_buf = buf;
+ recv_buf_len = len;
+ app_cb = cb;
+}
+
+int
+bttester_pipe_init(void)
+{
+ rtt_index_up = SEGGER_RTT_AllocUpBuffer(MYNEWT_VAL(BTTESTER_RTT_BUFFER_NAME),
+ rtt_buf_up, sizeof(rtt_buf_up),
+ SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL);
+
+ if (rtt_index_up < 0) {
+ return -1;
+ }
+
+ rtt_index_down = SEGGER_RTT_AllocDownBuffer(MYNEWT_VAL(BTTESTER_RTT_BUFFER_NAME),
+ rtt_buf_down, sizeof(rtt_buf_down),
+ SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL);
+
+ if (rtt_index_down < 0) {
+ return -1;
+ }
+
+ console_printf("Using up-buffer #%d\n", rtt_index_up);
+ console_printf("Using down-buffer #%d\n", rtt_index_down);
+
+ os_cputime_timer_init(&rtt_timer, rtt_pipe_poll_func, NULL);
+ os_cputime_timer_relative(&rtt_timer, 200000);
+ return 0;
+}
+#endif /* MYNEWT_VAL(BTTESTER_PIPE_RTT) */
diff --git a/src/libs/mynewt-nimble/apps/bttester/src/uart_pipe.c b/src/libs/mynewt-nimble/apps/bttester/src/uart_pipe.c
new file mode 100644
index 00000000..ecbefa02
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/src/uart_pipe.c
@@ -0,0 +1,281 @@
+/*
+ * 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 "syscfg/syscfg.h"
+
+#if MYNEWT_VAL(BTTESTER_PIPE_UART)
+
+#include "os/mynewt.h"
+#include "uart/uart.h"
+
+#include "bttester_pipe.h"
+
+static u8_t *recv_buf;
+static size_t recv_buf_len;
+static bttester_pipe_recv_cb app_cb;
+static size_t recv_off;
+
+struct uart_pipe_ring {
+ uint8_t head;
+ uint8_t tail;
+ uint16_t size;
+ uint8_t *buf;
+};
+
+static struct uart_dev *uart_dev;
+static struct uart_pipe_ring cr_tx;
+static uint8_t cr_tx_buf[MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX)];
+typedef void (*console_write_char)(struct uart_dev*, uint8_t);
+static console_write_char write_char_cb;
+
+static struct uart_pipe_ring cr_rx;
+static uint8_t cr_rx_buf[MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX)];
+static volatile bool uart_console_rx_stalled;
+
+struct os_event rx_ev;
+
+static inline int
+inc_and_wrap(int i, int max)
+{
+ return (i + 1) & (max - 1);
+}
+
+static void
+uart_pipe_ring_add_char(struct uart_pipe_ring *cr, char ch)
+{
+ cr->buf[cr->head] = ch;
+ cr->head = inc_and_wrap(cr->head, cr->size);
+}
+
+static uint8_t
+uart_pipe_ring_pull_char(struct uart_pipe_ring *cr)
+{
+ uint8_t ch;
+
+ ch = cr->buf[cr->tail];
+ cr->tail = inc_and_wrap(cr->tail, cr->size);
+ return ch;
+}
+
+static bool
+uart_pipe_ring_is_full(const struct uart_pipe_ring *cr)
+{
+ return inc_and_wrap(cr->head, cr->size) == cr->tail;
+}
+
+static bool
+uart_pipe_ring_is_empty(const struct uart_pipe_ring *cr)
+{
+ return cr->head == cr->tail;
+}
+
+static void
+uart_pipe_queue_char(struct uart_dev *uart_dev, uint8_t ch)
+{
+ int sr;
+
+ if ((uart_dev->ud_dev.od_flags & OS_DEV_F_STATUS_OPEN) == 0) {
+ return;
+ }
+
+ OS_ENTER_CRITICAL(sr);
+ while (uart_pipe_ring_is_full(&cr_tx)) {
+ /* TX needs to drain */
+ uart_start_tx(uart_dev);
+ OS_EXIT_CRITICAL(sr);
+ if (os_started()) {
+ os_time_delay(1);
+ }
+ OS_ENTER_CRITICAL(sr);
+ }
+ uart_pipe_ring_add_char(&cr_tx, ch);
+ OS_EXIT_CRITICAL(sr);
+}
+
+/*
+ * Interrupts disabled when console_tx_char/console_rx_char are called.
+ * Characters sent only in blocking mode.
+ */
+static int
+uart_console_tx_char(void *arg)
+{
+ if (uart_pipe_ring_is_empty(&cr_tx)) {
+ return -1;
+ }
+ return uart_pipe_ring_pull_char(&cr_tx);
+}
+
+/*
+ * Interrupts disabled when console_tx_char/console_rx_char are called.
+ */
+static int
+uart_console_rx_char(void *arg, uint8_t byte)
+{
+ if (uart_pipe_ring_is_full(&cr_rx)) {
+ uart_console_rx_stalled = true;
+ return -1;
+ }
+
+ uart_pipe_ring_add_char(&cr_rx, byte);
+
+ if (!rx_ev.ev_queued) {
+ os_eventq_put(os_eventq_dflt_get(), &rx_ev);
+ }
+
+ return 0;
+}
+
+static int
+uart_pipe_handle_char(int key)
+{
+ recv_buf[recv_off] = (u8_t) key;
+ recv_off++;
+
+ return 0;
+}
+
+static void
+uart_console_rx_char_event(struct os_event *ev)
+{
+ static int b = -1;
+ int sr;
+ int ret;
+
+ /* We may have unhandled character - try it first */
+ if (b >= 0) {
+ ret = uart_pipe_handle_char(b);
+ if (ret < 0) {
+ return;
+ }
+ }
+
+ while (!uart_pipe_ring_is_empty(&cr_rx)) {
+ OS_ENTER_CRITICAL(sr);
+ b = uart_pipe_ring_pull_char(&cr_rx);
+ OS_EXIT_CRITICAL(sr);
+
+ /* If UART RX was stalled due to a full receive buffer, restart RX now
+ * that we have removed a byte from the buffer.
+ */
+ if (uart_console_rx_stalled) {
+ uart_console_rx_stalled = false;
+ uart_start_rx(uart_dev);
+ }
+
+ ret = uart_pipe_handle_char(b);
+ if (ret < 0) {
+ return;
+ }
+ }
+
+ /*
+ * Call application callback with received data. Application
+ * may provide new buffer or alter data offset.
+ */
+ recv_buf = app_cb(recv_buf, &recv_off);
+
+ b = -1;
+}
+
+int
+bttester_pipe_send(const u8_t *data, int len)
+{
+ int i;
+
+ /* Assure that there is a write cb installed; this enables to debug
+ * code that is faulting before the console was initialized.
+ */
+ if (!write_char_cb) {
+ return -1;
+ }
+
+ for (i = 0; i < len; ++i) {
+ write_char_cb(uart_dev, data[i]);
+ }
+
+ uart_start_tx(uart_dev);
+
+ return 0;
+}
+
+int
+bttester_pipe_send_buf(struct os_mbuf *buf)
+{
+ int i, len;
+ struct os_mbuf *om;
+
+ /* Assure that there is a write cb installed; this enables to debug
+ * code that is faulting before the console was initialized.
+ */
+ if (!write_char_cb) {
+ return -1;
+ }
+
+ for (om = buf; om; om = SLIST_NEXT(om, om_next)) {
+ len = om->om_len;
+ for (i = 0; i < len; ++i) {
+ write_char_cb(uart_dev, om->om_data[i]);
+ }
+ }
+
+ uart_start_tx(uart_dev);
+
+ return 0;
+}
+
+int
+bttester_pipe_init(void)
+{
+ struct uart_conf uc = {
+ .uc_speed = MYNEWT_VAL(CONSOLE_UART_BAUD),
+ .uc_databits = 8,
+ .uc_stopbits = 1,
+ .uc_parity = UART_PARITY_NONE,
+ .uc_flow_ctl = MYNEWT_VAL(CONSOLE_UART_FLOW_CONTROL),
+ .uc_tx_char = uart_console_tx_char,
+ .uc_rx_char = uart_console_rx_char,
+ };
+
+ cr_tx.size = sizeof(cr_tx_buf);
+ cr_tx.buf = cr_tx_buf;
+ write_char_cb = uart_pipe_queue_char;
+
+ cr_rx.size = sizeof(cr_rx_buf);
+ cr_rx.buf = cr_rx_buf;
+
+ rx_ev.ev_cb = uart_console_rx_char_event;
+
+ if (!uart_dev) {
+ uart_dev = (struct uart_dev *)os_dev_open(MYNEWT_VAL(CONSOLE_UART_DEV),
+ OS_TIMEOUT_NEVER, &uc);
+ if (!uart_dev) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void
+bttester_pipe_register(u8_t *buf, size_t len, bttester_pipe_recv_cb cb)
+{
+ recv_buf = buf;
+ recv_buf_len = len;
+ app_cb = cb;
+}
+#endif /* MYNEWT_VAL(BTTESTER_PIPE_UART) */
diff --git a/src/libs/mynewt-nimble/apps/bttester/syscfg.yml b/src/libs/mynewt-nimble/apps/bttester/syscfg.yml
new file mode 100644
index 00000000..d0fffe13
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/bttester/syscfg.yml
@@ -0,0 +1,122 @@
+# 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.
+#
+
+# Package: apps/blemesh
+
+syscfg.defs:
+ BTTESTER_PIPE_UART:
+ description: 'Set communication pipe to UART'
+ value: 1
+
+ BTTESTER_PIPE_RTT:
+ description: 'Set communication pipe to RTT'
+ value: 0
+
+ BTTESTER_RTT_BUFFER_NAME:
+ description: Bttester rtt pipe buffer name
+ value: '"bttester"'
+
+ BTTESTER_RTT_BUFFER_SIZE_UP:
+ description: Bttester upstream buffer size
+ value: MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX)
+
+ BTTESTER_RTT_BUFFER_SIZE_DOWN:
+ description: Bttester downstream buffer size
+ value: MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX)
+
+ BTTESTER_PRIVACY_MODE:
+ description: Enable privacy mode (RPA or NRPA)
+ value: 0
+
+ BTTESTER_USE_NRPA:
+ description: Use Non Resolvable Private Address
+ value: 0
+
+ BTTESTER_LTD_ADV_TIMEOUT:
+ description: Limited advertising timeout
+ value: 30000
+
+ BTTESTER_CONN_RETRY:
+ description: Retry connections when connection failed to be established
+ value: 3
+
+ BTTESTER_BTP_DATA_SIZE_MAX:
+ description: Maximum BTP payload
+ value: 2048
+
+ BTTESTER_CONN_PARAM_UPDATE:
+ description: Trigger conn param update after connection establish
+ value: 0
+
+ BTTESTER_DEBUG:
+ description: Enable debug logging
+ value: 0
+
+ BTTESTER_BTP_LOG:
+ description: Enable logging BTP traffic
+ value: 0
+
+syscfg.vals:
+ OS_MAIN_STACK_SIZE: 512
+ SHELL_TASK: 0
+ SHELL_NEWTMGR: 0
+ LOG_LEVEL: 12
+ MSYS_1_BLOCK_COUNT: 48
+
+ BLE_MONITOR_RTT: 1
+ CONSOLE_RTT: 0
+ CONSOLE_UART: 0
+ RTT_NUM_BUFFERS_UP: 0
+ RTT_NUM_BUFFERS_DOWN: 0
+
+ BLE_L2CAP_COC_MAX_NUM: 2
+ BLE_L2CAP_SIG_MAX_PROCS: 2
+ # Some testcases require MPS < MTU
+ BLE_L2CAP_COC_MPS: 100
+ BLE_RPA_TIMEOUT: 30
+ BLE_SM_BONDING: 1
+ BLE_SM_MITM: 0
+ BLE_SM_SC: 1
+ BLE_SM_OUR_KEY_DIST: 7
+ BLE_SM_THEIR_KEY_DIST: 7
+ BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION: 1
+ BLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL: 9
+ BLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL: 30
+ BLE_SVC_GAP_PPCP_SUPERVISION_TMO: 2000
+
+ BLE_MESH: 1
+ BLE_MESH_SHELL: 0
+ BLE_MESH_PROV: 1
+ BLE_MESH_RELAY: 1
+ BLE_MESH_PB_ADV: 1
+ BLE_MESH_PB_GATT: 1
+ BLE_MESH_LOW_POWER: 1
+ BLE_MESH_LPN_AUTO: 0
+ BLE_MESH_GATT_PROXY: 1
+ BLE_MESH_LABEL_COUNT: 2
+ BLE_MESH_SUBNET_COUNT: 2
+ BLE_MESH_MODEL_GROUP_COUNT: 2
+ BLE_MESH_APP_KEY_COUNT: 4
+ BLE_MESH_IV_UPDATE_TEST: 1
+ BLE_MESH_TESTING: 1
+ BLE_MESH_FRIEND: 1
+ BLE_MESH_CFG_CLI: 1
+ BLE_MESH_RX_SDU_MAX: 110
+
+ BLE_MESH_ADV_BUF_COUNT: 20
+ BLE_MESH_TX_SEG_MAX: 6
diff --git a/src/libs/mynewt-nimble/apps/ext_advertiser/pkg.yml b/src/libs/mynewt-nimble/apps/ext_advertiser/pkg.yml
new file mode 100644
index 00000000..097764b2
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/ext_advertiser/pkg.yml
@@ -0,0 +1,40 @@
+# 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/ext_advertiser
+pkg.type: app
+pkg.description: Extended Advertising sample application.
+pkg.author: "Szymon Janc"
+pkg.email: "szymon.janc@codecoup.pl"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+ - nimble/controller
+ - nimble/host
+ - nimble/host/util
+ - nimble/host/services/gap
+ - nimble/host/services/gatt
+ - nimble/host/store/config
+ - nimble/transport/ram
+ - "@apache-mynewt-core/kernel/os"
+ - "@apache-mynewt-core/sys/console/full"
+ - "@apache-mynewt-core/sys/log/full"
+ - "@apache-mynewt-core/sys/stats/full"
+ - "@apache-mynewt-core/sys/sysinit"
+ - "@apache-mynewt-core/sys/id"
diff --git a/src/libs/mynewt-nimble/apps/ext_advertiser/src/main.c b/src/libs/mynewt-nimble/apps/ext_advertiser/src/main.c
new file mode 100644
index 00000000..6bbc23d5
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/ext_advertiser/src/main.c
@@ -0,0 +1,464 @@
+/*
+ * 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 "os/mynewt.h"
+#include "console/console.h"
+#include "config/config.h"
+#include "nimble/ble.h"
+#include "host/ble_hs.h"
+#include "host/util/util.h"
+
+#include"patterns.h"
+
+static uint8_t id_addr_type;
+
+static void start_legacy_duration(uint8_t pattern, bool configure);
+static void start_ext_max_events(uint8_t pattern, bool configure);
+
+static int
+start_ext_max_events_gap_event(struct ble_gap_event *event, void *arg)
+{
+ static uint8_t pattern = 1;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_ADV_COMPLETE:
+ break;
+ default:
+ assert(0);
+ return 0;
+ }
+
+ assert(event->adv_complete.instance == 4);
+ assert(event->adv_complete.reason == BLE_HS_ETIMEOUT);
+ assert(event->adv_complete.num_ext_adv_events == 10);
+
+ console_printf("instance %u terminated\n", event->adv_complete.instance);
+
+ pattern++;
+
+ start_ext_max_events(pattern, false);
+
+ return 0;
+}
+
+/* Starts advertising instance with 100 max events and changing adv data pattern
+ * and SID.
+ */
+static void
+start_ext_max_events(uint8_t pattern, bool configure)
+{
+ struct ble_gap_ext_adv_params params;
+ static uint8_t adv_data[600];
+ struct os_mbuf *data;
+ uint8_t instance = 4;
+ ble_addr_t addr;
+ int events = 10;
+ int rc;
+
+ if (configure) {
+ /* use defaults for non-set params */
+ memset (&params, 0, sizeof(params));
+
+ /* advertise using random addr */
+ params.own_addr_type = BLE_OWN_ADDR_RANDOM;
+
+ params.primary_phy = BLE_HCI_LE_PHY_1M;
+ params.secondary_phy = BLE_HCI_LE_PHY_1M;
+ params.tx_power = 127;
+ params.sid = pattern % 16;
+
+ /* allow larger interval, 400 * 0.625ms with 100 events will give up to
+ * ~2.5 seconds for instance
+ */
+ params.itvl_min = BLE_GAP_ADV_FAST_INTERVAL1_MIN;
+ params.itvl_max = 400;
+
+ /* configure instance 0 */
+ rc = ble_gap_ext_adv_configure(instance, &params, NULL,
+ start_ext_max_events_gap_event, NULL);
+ assert (rc == 0);
+
+ /* set random (NRPA) address for instance */
+ rc = ble_hs_id_gen_rnd(1, &addr);
+ assert (rc == 0);
+
+ rc = ble_gap_ext_adv_set_addr(instance, &addr );
+ assert (rc == 0);
+ }
+
+ /* in this case both advertising data and scan response is allowed, but
+ * both are limited to 31 bytes each
+ */
+
+ /* get mbuf for adv data */
+ data = os_msys_get_pkthdr(600, 0);
+ assert(data);
+
+ memset(adv_data, pattern, sizeof(adv_data));
+
+ /* fill mbuf with adv data */
+ rc = os_mbuf_append(data, adv_data, 600);
+ assert(rc == 0);
+
+ rc = ble_gap_ext_adv_set_data(instance, data);
+ assert (rc == 0);
+
+ /* start advertising */
+ rc = ble_gap_ext_adv_start(instance, 0, events);
+ assert (rc == 0);
+
+ console_printf("instance %u started (PDUs with max events %d)\n",
+ instance, events);
+}
+
+static int
+start_legacy_duration_gap_event(struct ble_gap_event *event, void *arg)
+{
+ static uint8_t pattern = 1;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_ADV_COMPLETE:
+ break;
+ default:
+ assert(0);
+ return 0;
+ }
+
+ assert(event->adv_complete.instance == 3);
+ assert(event->adv_complete.reason == BLE_HS_ETIMEOUT);
+
+ console_printf("instance %u terminated\n", event->adv_complete.instance);
+
+ pattern++;
+
+ start_legacy_duration(pattern, false);
+
+ return 0;
+}
+
+/* Starts advertising instance with 5sec timeout and changing adv data pattern
+ * and SID.
+ */
+static void
+start_legacy_duration(uint8_t pattern, bool configure)
+{
+ struct ble_gap_ext_adv_params params;
+ uint8_t adv_data[31];
+ struct os_mbuf *data;
+ uint8_t instance = 3;
+ ble_addr_t addr;
+ int duration = 500; /* 5seconds, 10ms units */
+ int rc;
+
+ if (configure) {
+ /* use defaults for non-set params */
+ memset (&params, 0, sizeof(params));
+
+ /* enable advertising using legacy PDUs */
+ params.legacy_pdu = 1;
+
+ /* advertise using random addr */
+ params.own_addr_type = BLE_OWN_ADDR_RANDOM;
+
+ params.primary_phy = BLE_HCI_LE_PHY_1M;
+ params.secondary_phy = BLE_HCI_LE_PHY_1M;
+ params.tx_power = 127;
+ params.sid = pattern % 16;
+
+ /* configure instance 0 */
+ rc = ble_gap_ext_adv_configure(instance, &params, NULL,
+ start_legacy_duration_gap_event, NULL);
+ assert (rc == 0);
+
+ /* set random (NRPA) address for instance */
+ rc = ble_hs_id_gen_rnd(1, &addr);
+ assert (rc == 0);
+
+ rc = ble_gap_ext_adv_set_addr(instance, &addr );
+ assert (rc == 0);
+ }
+
+ /* in this case both advertising data and scan response is allowed, but
+ * both are limited to 31 bytes each
+ */
+
+ /* get mbuf for adv data */
+ data = os_msys_get_pkthdr(31, 0);
+ assert(data);
+
+ memset(adv_data, pattern, sizeof(adv_data));
+
+ /* fill mbuf with adv data */
+ rc = os_mbuf_append(data, adv_data, 31);
+ assert(rc == 0);
+
+ rc = ble_gap_ext_adv_set_data(instance, data);
+ assert (rc == 0);
+
+ /* start advertising */
+ rc = ble_gap_ext_adv_start(instance, duration, 0);
+ assert (rc == 0);
+
+ console_printf("instance %u started (legacy PDUs with duration %d)\n",
+ instance, duration);
+}
+
+/* this is simple non-connectable scannable instance using legacy PUDs that
+ * runs forever
+ */
+static void
+start_scannable_legacy_ext(void)
+{
+ struct ble_gap_ext_adv_params params;
+ struct os_mbuf *data;
+ uint8_t instance = 2;
+ ble_addr_t addr;
+ int rc;
+
+ /* use defaults for non-set params */
+ memset (&params, 0, sizeof(params));
+
+ /* enable scannable advertising using legacy PDUs */
+ params.scannable = 1;
+ params.legacy_pdu = 1;
+
+ /* advertise using random addr */
+ params.own_addr_type = BLE_OWN_ADDR_RANDOM;
+
+ params.primary_phy = BLE_HCI_LE_PHY_1M;
+ params.secondary_phy = BLE_HCI_LE_PHY_1M;
+ params.tx_power = 127;
+ params.sid = 2;
+
+ /* configure instance 0 */
+ rc = ble_gap_ext_adv_configure(instance, &params, NULL, NULL, NULL);
+ assert (rc == 0);
+
+ /* set random (NRPA) address for instance */
+ rc = ble_hs_id_gen_rnd(1, &addr);
+ assert (rc == 0);
+
+ rc = ble_gap_ext_adv_set_addr(instance, &addr );
+ assert (rc == 0);
+
+ /* in this case both advertising data and scan response is allowed, but
+ * both are limited to 31 bytes each
+ */
+
+ /* get mbuf for adv data */
+ data = os_msys_get_pkthdr(31, 0);
+ assert(data);
+
+ /* fill mbuf with adv data */
+ rc = os_mbuf_append(data, ext_adv_pattern_1, 31);
+ assert(rc == 0);
+
+ rc = ble_gap_ext_adv_set_data(instance, data);
+ assert (rc == 0);
+
+ /* get mbuf for scan rsp data */
+ data = os_msys_get_pkthdr(31, 0);
+ assert(data);
+
+ /* fill mbuf with scan rsp data */
+ rc = os_mbuf_append(data, ext_adv_pattern_1 + 31, 31);
+ assert(rc == 0);
+
+ rc = ble_gap_ext_adv_rsp_set_data(instance, data);
+ assert (rc == 0);
+
+ /* start advertising */
+ rc = ble_gap_ext_adv_start(instance, 0, 0);
+ assert (rc == 0);
+
+ console_printf("instance %u started (scannable legacy PDUs)\n", instance);
+}
+
+static int
+scannable_ext_gap_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* this is simple scannable instance that runs forever
+ * TODO Get scan request notifications.
+ */
+static void
+start_scannable_ext(void)
+{
+ struct ble_gap_ext_adv_params params;
+ struct os_mbuf *data;
+ uint8_t instance = 1;
+ ble_addr_t addr;
+ int rc;
+
+ /* use defaults for non-set params */
+ memset (&params, 0, sizeof(params));
+
+ /* enable scannable advertising */
+ params.scannable = 1;
+
+ /* enable scan request notification */
+ params.scan_req_notif = 1;
+
+ /* advertise using random addr */
+ params.own_addr_type = BLE_OWN_ADDR_RANDOM;
+
+ params.primary_phy = BLE_HCI_LE_PHY_1M;
+ params.secondary_phy = BLE_HCI_LE_PHY_1M;
+ params.tx_power = 127;
+ params.sid = 1;
+
+ /* configure instance 0 */
+ rc = ble_gap_ext_adv_configure(instance, &params, NULL,
+ scannable_ext_gap_event, NULL);
+ assert (rc == 0);
+
+ /* set random (NRPA) address for instance */
+ rc = ble_hs_id_gen_rnd(1, &addr);
+ assert (rc == 0);
+
+ rc = ble_gap_ext_adv_set_addr(instance, &addr );
+ assert (rc == 0);
+
+ /* in this case only scan response is allowed */
+
+ /* get mbuf for scan rsp data */
+ data = os_msys_get_pkthdr(sizeof(ext_adv_pattern_1), 0);
+ assert(data);
+
+ /* fill mbuf with scan rsp data */
+ rc = os_mbuf_append(data, ext_adv_pattern_1, sizeof(ext_adv_pattern_1));
+ assert(rc == 0);
+
+ rc = ble_gap_ext_adv_rsp_set_data(instance, data);
+ assert (rc == 0);
+
+ /* start advertising */
+ rc = ble_gap_ext_adv_start(instance, 0, 0);
+ assert (rc == 0);
+
+ console_printf("instance %u started (scannable)\n", instance);
+}
+
+/* this is simple non-connectable instance that runs forever */
+static void
+start_non_connectable_ext(void)
+{
+ struct ble_gap_ext_adv_params params;
+ struct os_mbuf *data;
+ uint8_t instance = 0;
+ int rc;
+
+ /* use defaults for non-set params */
+ memset (&params, 0, sizeof(params));
+
+ /* advertise using ID addr */
+ params.own_addr_type = id_addr_type;
+
+ params.primary_phy = BLE_HCI_LE_PHY_1M;
+ params.secondary_phy = BLE_HCI_LE_PHY_1M;
+ params.tx_power = 127;
+ params.sid = 0;
+
+ /* configure instance */
+ rc = ble_gap_ext_adv_configure(instance, &params, NULL, NULL, NULL);
+ assert (rc == 0);
+
+ /* in this case only advertisign data is allowed */
+
+ /* get mbuf for adv data */
+ data = os_msys_get_pkthdr(sizeof(ext_adv_pattern_1), 0);
+ assert(data);
+
+ /* fill mbuf with adv data */
+ rc = os_mbuf_append(data, ext_adv_pattern_1, sizeof(ext_adv_pattern_1));
+ assert(rc == 0);
+
+ rc = ble_gap_ext_adv_set_data(instance, data);
+ assert (rc == 0);
+
+ /* start advertising */
+ rc = ble_gap_ext_adv_start(instance, 0, 0);
+ assert (rc == 0);
+
+ console_printf("instance %u started (non-con non-scan)\n", instance);
+}
+
+static void
+on_sync(void)
+{
+ int rc;
+
+ console_printf("Synced, starting advertising\n");
+
+ /* Make sure we have proper identity address set (public preferred) */
+ rc = ble_hs_util_ensure_addr(0);
+ assert(rc == 0);
+
+ /* configure global address */
+ rc = ble_hs_id_infer_auto(0, &id_addr_type);
+ assert(rc == 0);
+
+ start_non_connectable_ext();
+
+ start_scannable_ext();
+
+ start_scannable_legacy_ext();
+
+ start_legacy_duration(0, true);
+
+ start_ext_max_events(0, true);
+}
+
+/*
+ * 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)
+{
+ /* Initialize OS */
+ sysinit();
+
+ console_printf("Extended Advertising sample application\n");
+
+ /* Set sync callback */
+ ble_hs_cfg.sync_cb = on_sync;
+
+ /* As the last thing, process events from default event queue */
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/ext_advertiser/src/patterns.h b/src/libs/mynewt-nimble/apps/ext_advertiser/src/patterns.h
new file mode 100644
index 00000000..9485d0d4
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/ext_advertiser/src/patterns.h
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+
+static const uint8_t ext_adv_pattern_1[] = {
+ 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0a,
+ 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14,
+ 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1e,
+ 0x00, 0x20, 0x00, 0x22, 0x00, 0x24, 0x00, 0x26, 0x00, 0x28,
+ 0x00, 0x2a, 0x00, 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32,
+ 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c,
+ 0x00, 0x3e, 0x00, 0x40, 0x00, 0x42, 0x00, 0x44, 0x00, 0x46,
+ 0x00, 0x48, 0x00, 0x4a, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50,
+ 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x58, 0x00, 0x5a,
+ 0x00, 0x5c, 0x00, 0x5e, 0x00, 0x60, 0x00, 0x62, 0x00, 0x64,
+ 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, 0x6c, 0x00, 0x6e,
+ 0x00, 0x70, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x78,
+ 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x82,
+ 0x00, 0x84, 0x00, 0x86, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x8c,
+ 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96,
+ 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa0,
+ 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa,
+ 0x00, 0xac, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4,
+ 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe,
+ 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc8,
+ 0x00, 0xca, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd2,
+ 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdc,
+ 0x00, 0xde, 0x00, 0xe0, 0x00, 0xe2, 0x00, 0xe4, 0x00, 0xe6,
+ 0x00, 0xe8, 0x00, 0xea, 0x00, 0xec, 0x00, 0xee, 0x00, 0xf0,
+ 0x00, 0xf2, 0x00, 0xf4, 0x00, 0xf6, 0x00, 0xf8, 0x00, 0xfa,
+ 0x00, 0xfc, 0x00, 0xfe, 0x01, 0x01, 0x01, 0x03, 0x01, 0x05,
+ 0x01, 0x07, 0x01, 0x09, 0x01, 0x0b, 0x01, 0x0d, 0x01, 0x0f,
+ 0x01, 0x11, 0x01, 0x13, 0x01, 0x15, 0x01, 0x17, 0x01, 0x19,
+ 0x01, 0x1b, 0x01, 0x1d, 0x01, 0x1f, 0x01, 0x21, 0x01, 0x23,
+ 0x01, 0x25, 0x01, 0x27, 0x01, 0x29, 0x01, 0x2b, 0x01, 0x2d,
+ 0x01, 0x2f, 0x01, 0x31, 0x01, 0x33, 0x01, 0x35, 0x01, 0x37,
+ 0x01, 0x39, 0x01, 0x3b, 0x01, 0x3d, 0x01, 0x3f, 0x01, 0x41,
+ 0x01, 0x43, 0x01, 0x45, 0x01, 0x47, 0x01, 0x49, 0x01, 0x4b,
+ 0x01, 0x4d, 0x01, 0x4f, 0x01, 0x51, 0x01, 0x53, 0x01, 0x55,
+ 0x01, 0x57, 0x01, 0x59, 0x01, 0x5b, 0x01, 0x5d, 0x01, 0x5f,
+ 0x01, 0x61, 0x01, 0x63, 0x01, 0x65, 0x01, 0x67, 0x01, 0x69,
+ 0x01, 0x6b, 0x01, 0x6d, 0x01, 0x6f, 0x01, 0x71, 0x01, 0x73,
+ 0x01, 0x75, 0x01, 0x77, 0x01, 0x79, 0x01, 0x7b, 0x01, 0x7d,
+ 0x01, 0x7f, 0x01, 0x81, 0x01, 0x83, 0x01, 0x85, 0x01, 0x87,
+ 0x01, 0x89, 0x01, 0x8b, 0x01, 0x8d, 0x01, 0x8f, 0x01, 0x91,
+ 0x01, 0x93, 0x01, 0x95, 0x01, 0x97, 0x01, 0x99, 0x01, 0x9b,
+ 0x01, 0x9d, 0x01, 0x9f, 0x01, 0xa1, 0x01, 0xa3, 0x01, 0xa5,
+ 0x01, 0xa7, 0x01, 0xa9, 0x01, 0xab, 0x01, 0xad, 0x01, 0xaf,
+ 0x01, 0xb1, 0x01, 0xb3, 0x01, 0xb5, 0x01, 0xb7, 0x01, 0xb9,
+ 0x01, 0xbb, 0x01, 0xbd, 0x01, 0xbf, 0x01, 0xc1, 0x01, 0xc3,
+ 0x01, 0xc5, 0x01, 0xc7, 0x01, 0xc9, 0x01, 0xcb, 0x01, 0xcd,
+ 0x01, 0xcf, 0x01, 0xd1, 0x01, 0xd3, 0x01, 0xd5, 0x01, 0xd7,
+ 0x01, 0xd9, 0x01, 0xdb, 0x01, 0xdd, 0x01, 0xdf, 0x01, 0xe1,
+ 0x01, 0xe3, 0x01, 0xe5, 0x01, 0xe7, 0x01, 0xe9, 0x01, 0xeb,
+ 0x01, 0xed, 0x01, 0xef, 0x01, 0xf1, 0x01, 0xf3, 0x01, 0xf5,
+ 0x01, 0xf7, 0x01, 0xf9, 0x01, 0xfb, 0x01, 0xfd, 0x02, 0x00,
+ 0x02, 0x02, 0x02, 0x04, 0x02, 0x06, 0x02, 0x08, 0x02, 0x0a,
+ 0x02, 0x0c, 0x02, 0x0e, 0x02, 0x10, 0x02, 0x12, 0x02, 0x14,
+ 0x02, 0x16, 0x02, 0x18, 0x02, 0x1a, 0x02, 0x1c, 0x02, 0x1e,
+ 0x02, 0x20, 0x02, 0x22, 0x02, 0x24, 0x02, 0x26, 0x02, 0x28,
+ 0x02, 0x2a, 0x02, 0x2c, 0x02, 0x2e, 0x02, 0x30, 0x02, 0x32,
+ 0x02, 0x34, 0x02, 0x36, 0x02, 0x38, 0x02, 0x3a, 0x02, 0x3c,
+ 0x02, 0x3e, 0x02, 0x40, 0x02, 0x42, 0x02, 0x44, 0x02, 0x46,
+ 0x02, 0x48, 0x02, 0x4a, 0x02, 0x4c, 0x02, 0x4e, 0x02, 0x50,
+ 0x02, 0x52, 0x02, 0x54, 0x02, 0x56, 0x02, 0x58, 0x02, 0x5a,
+ 0x02, 0x5c, 0x02, 0x5e, 0x02, 0x60, 0x02, 0x62, 0x02, 0x64,
+ 0x02, 0x66, 0x02, 0x68, 0x02, 0x6a, 0x02, 0x6c, 0x02, 0x6e,
+ 0x02, 0x70, 0x02, 0x72, 0x02, 0x74, 0x02, 0x76, 0x02, 0x78,
+ 0x02, 0x7a, 0x02, 0x7c, 0x02, 0x7e, 0x02, 0x80, 0x02, 0x82,
+ 0x02, 0x84, 0x02, 0x86, 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c,
+ 0x02, 0x8e, 0x02, 0x90, 0x02, 0x92, 0x02, 0x94, 0x02, 0x96,
+ 0x02, 0x98, 0x02, 0x9a, 0x02, 0x9c, 0x02, 0x9e, 0x02, 0xa0,
+ 0x02, 0xa2, 0x02, 0xa4, 0x02, 0xa6, 0x02, 0xa8, 0x02, 0xaa,
+ 0x02, 0xac, 0x02, 0xae, 0x02, 0xb0, 0x02, 0xb2, 0x02, 0xb4,
+ 0x02, 0xb6, 0x02, 0xb8, 0x02, 0xba, 0x02, 0xbc, 0x02, 0xbe,
+ 0x02, 0xc0, 0x02, 0xc2, 0x02, 0xc4, 0x02, 0xc6, 0x02, 0xc8,
+ 0x02, 0xca, 0x02, 0xcc, 0x02, 0xce, 0x02, 0xd0, 0x02, 0xd2,
+ 0x02, 0xd4, 0x02, 0xd6, 0x02, 0xd8, 0x02, 0xda, 0x02, 0xdc,
+ 0x02, 0xde, 0x02, 0xe0, 0x02, 0xe2, 0x02, 0xe4, 0x02, 0xe6,
+ 0x02, 0xe8, 0x02, 0xea, 0x02, 0xec, 0x02, 0xee, 0x02, 0xf0,
+ 0x02, 0xf2, 0x02, 0xf4, 0x02, 0xf6, 0x02, 0xf8, 0x02, 0xfa,
+ 0x02, 0xfc, 0x02, 0xfe, 0x03, 0x01, 0x03, 0x03, 0x03, 0x05,
+ 0x03, 0x07, 0x03, 0x09, 0x03, 0x0b, 0x03, 0x0d, 0x03, 0x0f,
+ 0x03, 0x11, 0x03, 0x13, 0x03, 0x15, 0x03, 0x17, 0x03, 0x19,
+ 0x03, 0x1b, 0x03, 0x1d, 0x03, 0x1f, 0x03, 0x21, 0x03, 0x23,
+ 0x03, 0x25, 0x03, 0x27, 0x03, 0x29, 0x03, 0x2b, 0x03, 0x2d,
+ 0x03, 0x2f, 0x03, 0x31, 0x03, 0x33, 0x03, 0x35, 0x03, 0x37,
+ 0x03, 0x39, 0x03, 0x3b, 0x03, 0x3d, 0x03, 0x3f, 0x03, 0x41,
+ 0x03, 0x43, 0x03, 0x45, 0x03, 0x47, 0x03, 0x49, 0x03, 0x4b,
+ 0x03, 0x4d, 0x03, 0x4f, 0x03, 0x51, 0x03, 0x53, 0x03, 0x55,
+ 0x03, 0x57, 0x03, 0x59, 0x03, 0x5b, 0x03, 0x5d, 0x03, 0x5f,
+ 0x03, 0x61, 0x03, 0x63, 0x03, 0x65, 0x03, 0x67, 0x03, 0x69,
+ 0x03, 0x6b, 0x03, 0x6d, 0x03, 0x6f, 0x03, 0x71, 0x03, 0x73,
+ 0x03, 0x75, 0x03, 0x77, 0x03, 0x79, 0x03, 0x7b, 0x03, 0x7d,
+ 0x03, 0x7f, 0x03, 0x81, 0x03, 0x83, 0x03, 0x85, 0x03, 0x87,
+ 0x03, 0x89, 0x03, 0x8b, 0x03, 0x8d, 0x03, 0x8f, 0x03, 0x91,
+ 0x03, 0x93, 0x03, 0x95, 0x03, 0x97, 0x03, 0x99, 0x03, 0x9b,
+ 0x03, 0x9d, 0x03, 0x9f, 0x03, 0xa1, 0x03, 0xa3, 0x03, 0xa5,
+ 0x03, 0xa7, 0x03, 0xa9, 0x03, 0xab, 0x03, 0xad, 0x03, 0xaf,
+ 0x03, 0xb1, 0x03, 0xb3, 0x03, 0xb5, 0x03, 0xb7, 0x03, 0xb9,
+ 0x03, 0xbb, 0x03, 0xbd, 0x03, 0xbf, 0x03, 0xc1, 0x03, 0xc3,
+ 0x03, 0xc5, 0x03, 0xc7, 0x03, 0xc9, 0x03, 0xcb, 0x03, 0xcd,
+ 0x03, 0xcf, 0x03, 0xd1, 0x03, 0xd3, 0x03, 0xd5, 0x03, 0xd7,
+ 0x03, 0xd9, 0x03, 0xdb, 0x03, 0xdd, 0x03, 0xdf, 0x03, 0xe1,
+ 0x03, 0xe3, 0x03, 0xe5, 0x03, 0xe7, 0x03, 0xe9, 0x03, 0xeb,
+ 0x03, 0xed, 0x03, 0xef, 0x03, 0xf1, 0x03, 0xf3, 0x03, 0xf5,
+ 0x03, 0xf7, 0x03, 0xf9, 0x03, 0xfb, 0x03, 0xfd, 0x04, 0x00,
+ 0x04, 0x02, 0x04, 0x04, 0x04, 0x06, 0x04, 0x08, 0x04, 0x0a,
+ 0x04, 0x0c, 0x04, 0x0e, 0x04, 0x10, 0x04, 0x12, 0x04, 0x14,
+ 0x04, 0x16, 0x04, 0x18, 0x04, 0x1a, 0x04, 0x1c, 0x04, 0x1e,
+ 0x04, 0x20, 0x04, 0x22, 0x04, 0x24, 0x04, 0x26, 0x04, 0x28,
+ 0x04, 0x2a, 0x04, 0x2c, 0x04, 0x2e, 0x04, 0x30, 0x04, 0x32,
+ 0x04, 0x34, 0x04, 0x36, 0x04, 0x38, 0x04, 0x3a, 0x04, 0x3c,
+ 0x04, 0x3e, 0x04, 0x40, 0x04, 0x42, 0x04, 0x44, 0x04, 0x46,
+ 0x04, 0x48, 0x04, 0x4a, 0x04, 0x4c, 0x04, 0x4e, 0x04, 0x50,
+ 0x04, 0x52, 0x04, 0x54, 0x04, 0x56, 0x04, 0x58, 0x04, 0x5a,
+ 0x04, 0x5c, 0x04, 0x5e, 0x04, 0x60, 0x04, 0x62, 0x04, 0x64,
+ 0x04, 0x66, 0x04, 0x68, 0x04, 0x6a, 0x04, 0x6c, 0x04, 0x6e,
+ 0x04, 0x70, 0x04, 0x72, 0x04, 0x74, 0x04, 0x76, 0x04, 0x78,
+ 0x04, 0x7a, 0x04, 0x7c, 0x04, 0x7e, 0x04, 0x80, 0x04, 0x82,
+ 0x04, 0x84, 0x04, 0x86, 0x04, 0x88, 0x04, 0x8a, 0x04, 0x8c,
+ 0x04, 0x8e, 0x04, 0x90, 0x04, 0x92, 0x04, 0x94, 0x04, 0x96,
+ 0x04, 0x98, 0x04, 0x9a, 0x04, 0x9c, 0x04, 0x9e, 0x04, 0xa0,
+ 0x04, 0xa2, 0x04, 0xa4, 0x04, 0xa6, 0x04, 0xa8, 0x04, 0xaa,
+ 0x04, 0xac, 0x04, 0xae, 0x04, 0xb0, 0x04, 0xb2, 0x04, 0xb4,
+ 0x04, 0xb6, 0x04, 0xb8, 0x04, 0xba, 0x04, 0xbc, 0x04, 0xbe,
+ 0x04, 0xc0, 0x04, 0xc2, 0x04, 0xc4, 0x04, 0xc6, 0x04, 0xc8,
+ 0x04, 0xca, 0x04, 0xcc, 0x04, 0xce, 0x04, 0xd0, 0x04, 0xd2,
+ 0x04, 0xd4, 0x04, 0xd6, 0x04, 0xd8, 0x04, 0xda, 0x04, 0xdc,
+ 0x04, 0xde, 0x04, 0xe0, 0x04, 0xe2, 0x04, 0xe4, 0x04, 0xe6,
+ 0x04, 0xe8, 0x04, 0xea, 0x04, 0xec, 0x04, 0xee, 0x04, 0xf0,
+ 0x04, 0xf2, 0x04, 0xf4, 0x04, 0xf6, 0x04, 0xf8, 0x04, 0xfa,
+ 0x04, 0xfc, 0x04, 0xfe, 0x05, 0x01, 0x05, 0x03, 0x05, 0x05,
+ 0x05, 0x07, 0x05, 0x09, 0x05, 0x0b, 0x05, 0x0d, 0x05, 0x0f,
+ 0x05, 0x11, 0x05, 0x13, 0x05, 0x15, 0x05, 0x17, 0x05, 0x19,
+ 0x05, 0x1b, 0x05, 0x1d, 0x05, 0x1f, 0x05, 0x21, 0x05, 0x23,
+ 0x05, 0x25, 0x05, 0x27, 0x05, 0x29, 0x05, 0x2b, 0x05, 0x2d,
+ 0x05, 0x2f, 0x05, 0x31, 0x05, 0x33, 0x05, 0x35, 0x05, 0x37,
+ 0x05, 0x39, 0x05, 0x3b, 0x05, 0x3d, 0x05, 0x3f, 0x05, 0x41,
+ 0x05, 0x43, 0x05, 0x45, 0x05, 0x47, 0x05, 0x49, 0x05, 0x4b,
+ 0x05, 0x4d, 0x05, 0x4f, 0x05, 0x51, 0x05, 0x53, 0x05, 0x55,
+ 0x05, 0x57, 0x05, 0x59, 0x05, 0x5b, 0x05, 0x5d, 0x05, 0x5f,
+ 0x05, 0x61, 0x05, 0x63, 0x05, 0x65, 0x05, 0x67, 0x05, 0x69,
+ 0x05, 0x6b, 0x05, 0x6d, 0x05, 0x6f, 0x05, 0x71, 0x05, 0x73,
+ 0x05, 0x75, 0x05, 0x77, 0x05, 0x79, 0x05, 0x7b, 0x05, 0x7d,
+ 0x05, 0x7f, 0x05, 0x81, 0x05, 0x83, 0x05, 0x85, 0x05, 0x87,
+ 0x05, 0x89, 0x05, 0x8b, 0x05, 0x8d, 0x05, 0x8f, 0x05, 0x91,
+ 0x05, 0x93, 0x05, 0x95, 0x05, 0x97, 0x05, 0x99, 0x05, 0x9b,
+ 0x05, 0x9d, 0x05, 0x9f, 0x05, 0xa1, 0x05, 0xa3, 0x05, 0xa5,
+ 0x05, 0xa7, 0x05, 0xa9, 0x05, 0xab, 0x05, 0xad, 0x05, 0xaf,
+ 0x05, 0xb1, 0x05, 0xb3, 0x05, 0xb5, 0x05, 0xb7, 0x05, 0xb9,
+ 0x05, 0xbb, 0x05, 0xbd, 0x05, 0xbf, 0x05, 0xc1, 0x05, 0xc3,
+ 0x05, 0xc5, 0x05, 0xc7, 0x05, 0xc9, 0x05, 0xcb, 0x05, 0xcd,
+ 0x05, 0xcf, 0x05, 0xd1, 0x05, 0xd3, 0x05, 0xd5, 0x05, 0xd7,
+ 0x05, 0xd9, 0x05, 0xdb, 0x05, 0xdd, 0x05, 0xdf, 0x05, 0xe1,
+ 0x05, 0xe3, 0x05, 0xe5, 0x05, 0xe7, 0x05, 0xe9, 0x05, 0xeb,
+ 0x05, 0xed, 0x05, 0xef, 0x05, 0xf1, 0x05, 0xf3, 0x05, 0xf5,
+ 0x05, 0xf7, 0x05, 0xf9, 0x05, 0xfb, 0x05, 0xfd, 0x06, 0x00,
+ 0x06, 0x02, 0x06, 0x04, 0x06, 0x06, 0x06, 0x08, 0x06, 0x0a,
+ 0x06, 0x0c, 0x06, 0x0e, 0x06, 0x10, 0x06, 0x12, 0x06, 0x14,
+ 0x06, 0x16, 0x06, 0x18, 0x06, 0x1a, 0x06, 0x1c, 0x06, 0x1e,
+ 0x06, 0x20, 0x06, 0x22, 0x06, 0x24, 0x06, 0x26, 0x06, 0x28,
+ 0x06, 0x2a, 0x06, 0x2c, 0x06, 0x2e, 0x06, 0x30, 0x06, 0x32,
+ 0x06, 0x34, 0x06, 0x36, 0x06, 0x38, 0x06, 0x3a, 0x06, 0x3c,
+ 0x06, 0x3e, 0x06, 0x40, 0x06, 0x42, 0x06, 0x44, 0x06, 0x46,
+ 0x06, 0x48, 0x06, 0x4a, 0x06, 0x4c, 0x06, 0x4e, 0x06, 0x50,
+ 0x06, 0x52, 0x06, 0x54, 0x06, 0x56, 0x06, 0x58, 0x06, 0x5a,
+ 0x06, 0x5c, 0x06, 0x5e, 0x06, 0x60, 0x06, 0x62, 0x06, 0x64,
+ 0x06, 0x66, 0x06, 0x68, 0x06, 0x6a, 0x06, 0x6c, 0x06, 0x6e,
+ 0x06, 0x70, 0x06, 0x72, 0x06, 0x74, 0x06, 0x76, 0x06, 0x78
+};
diff --git a/src/libs/mynewt-nimble/apps/ext_advertiser/syscfg.yml b/src/libs/mynewt-nimble/apps/ext_advertiser/syscfg.yml
new file mode 100644
index 00000000..0702ea79
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/ext_advertiser/syscfg.yml
@@ -0,0 +1,45 @@
+# 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:
+ # Disable not used GAP roles (we only do non-connectable
+ # advertising here)
+ BLE_ROLE_BROADCASTER: 1
+ BLE_ROLE_CENTRAL: 0
+ BLE_ROLE_OBSERVER: 0
+ BLE_ROLE_PERIPHERAL: 0
+
+ # Disable unused eddystone featdure.
+ BLE_EDDYSTONE: 0
+
+ # Enable Extended Advertising
+ BLE_EXT_ADV: 1
+
+ # Max advertising data size
+ BLE_EXT_ADV_MAX_SIZE: 1650
+
+ # Number of multi-advertising instances. Note that due
+ # to historical reasonds total number of advertising
+ # instances is BLE_MULTI_ADV_INSTANCES + 1 as instance
+ # 0 is always available
+ BLE_MULTI_ADV_INSTANCES: 4
+
+ # Controller uses msys pool for storing advertising data and scan responses.
+ # Since we advertise a lot of data (~4k in total) at the same time we need
+ # to increase block count.
+ MSYS_1_BLOCK_COUNT: 24
diff --git a/src/libs/mynewt-nimble/apps/peripheral/pkg.yml b/src/libs/mynewt-nimble/apps/peripheral/pkg.yml
new file mode 100755
index 00000000..6167edab
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/peripheral/pkg.yml
@@ -0,0 +1,37 @@
+#
+# 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/peripheral"
+pkg.type: app
+pkg.description: "Basic perihperal application"
+pkg.author: "Krzysztof Kopyściński krzysztof.kopyscinski@codecoup.pl"
+
+
+pkg.deps:
+ - "@apache-mynewt-core/kernel/os"
+ - "@apache-mynewt-core/sys/console/full"
+ - "@apache-mynewt-core/sys/log/full"
+ - "@apache-mynewt-core/sys/stats/full"
+ - "@apache-mynewt-core/sys/log/modlog"
+ - "@apache-mynewt-nimble/nimble/host"
+ - "@apache-mynewt-nimble/nimble/host/util/"
+ - "@apache-mynewt-nimble/nimble/host/services/gap"
+ - "@apache-mynewt-nimble/nimble/host/store/config"
+ - "@apache-mynewt-nimble/nimble/transport"
+
diff --git a/src/libs/mynewt-nimble/apps/peripheral/src/main.c b/src/libs/mynewt-nimble/apps/peripheral/src/main.c
new file mode 100755
index 00000000..f22579a7
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/peripheral/src/main.c
@@ -0,0 +1,166 @@
+/*
+ * 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 <stdlib.h>
+
+#include "os/os.h"
+#include "sysinit/sysinit.h"
+#include "log/log.h"
+#include "host/ble_hs.h"
+#include "host/util/util.h"
+#include "services/gap/ble_svc_gap.h"
+
+static uint8_t g_own_addr_type;
+static uint16_t conn_handle;
+static const char *device_name = "Mynewt";
+
+/* adv_event() calls advertise(), so forward declaration is required */
+static void advertise(void);
+
+static int
+adv_event(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_ADV_COMPLETE:
+ MODLOG_DFLT(INFO,"Advertising completed, termination code: %d\n",
+ event->adv_complete.reason);
+ advertise();
+ break;
+ case BLE_GAP_EVENT_CONNECT:
+ assert(event->connect.status == 0);
+ MODLOG_DFLT(INFO, "connection %s; status=%d\n",
+ event->connect.status == 0 ? "established" : "failed",
+ event->connect.status);
+ break;
+ case BLE_GAP_EVENT_CONN_UPDATE_REQ:
+ /* connected device requests update of connection parameters,
+ and these are being filled in - NULL sets default values */
+ MODLOG_DFLT(INFO, "updating conncetion parameters...\n");
+ event->conn_update_req.conn_handle = conn_handle;
+ event->conn_update_req.peer_params = NULL;
+ MODLOG_DFLT(INFO, "connection parameters updated!\n");
+ break;
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "disconnect; reason=%d\n",
+ event->disconnect.reason);
+
+ /* reset conn_handle */
+ conn_handle = BLE_HS_CONN_HANDLE_NONE;
+
+ /* Connection terminated; resume advertising */
+ advertise();
+ break;
+ default:
+ MODLOG_DFLT(ERROR, "Advertising event not handled,"
+ "event code: %u\n", event->type);
+ break;
+ }
+ return 0;
+}
+
+static void
+advertise(void)
+{
+ int rc;
+
+ /* set adv parameters */
+ struct ble_gap_adv_params adv_params;
+ struct ble_hs_adv_fields fields;
+ /* advertising payload is split into advertising data and advertising
+ response, because all data cannot fit into single packet; name of device
+ is sent as response to scan request */
+ struct ble_hs_adv_fields rsp_fields;
+
+ /* fill all fields and parameters with zeros */
+ memset(&adv_params, 0, sizeof(adv_params));
+ memset(&fields, 0, sizeof(fields));
+ memset(&rsp_fields, 0, sizeof(rsp_fields));
+
+ adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
+ adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
+
+ fields.flags = BLE_HS_ADV_F_DISC_GEN |
+ BLE_HS_ADV_F_BREDR_UNSUP;
+ fields.uuids128 = BLE_UUID128(BLE_UUID128_DECLARE(
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff));
+ fields.num_uuids128 = 1;
+ fields.uuids128_is_complete = 0;;
+ fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
+
+ rsp_fields.name = (uint8_t *)device_name;
+ rsp_fields.name_len = strlen(device_name);
+ rsp_fields.name_is_complete = 1;
+
+ rc = ble_gap_adv_set_fields(&fields);
+ assert(rc == 0);
+
+ rc = ble_gap_adv_rsp_set_fields(&rsp_fields);
+
+ MODLOG_DFLT(INFO,"Starting advertising...\n");
+
+ rc = ble_gap_adv_start(g_own_addr_type, NULL, 100,
+ &adv_params, adv_event, NULL);
+ assert(rc == 0);
+}
+
+static void
+on_sync(void)
+{
+ int rc;
+
+ /* g_own_addr_type will store type of addres our BSP uses */
+ rc = ble_hs_util_ensure_addr(0);
+ assert(rc == 0);
+ rc = ble_hs_id_infer_auto(0, &g_own_addr_type);
+ assert(rc == 0);
+ /* begin advertising */
+ advertise();
+}
+
+static void
+on_reset(int reason)
+{
+ MODLOG_DFLT(INFO, "Resetting state; reason=%d\n", reason);
+}
+
+int
+main(int argc, char **argv)
+{
+ int rc;
+
+ /* Initialize all packages. */
+ sysinit();
+
+ ble_hs_cfg.sync_cb = on_sync;
+ ble_hs_cfg.reset_cb = on_reset;
+
+ rc = ble_svc_gap_device_name_set(device_name);
+ assert(rc == 0);
+
+ /* As the last thing, process events from default event queue. */
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/scanner/pkg.yml b/src/libs/mynewt-nimble/apps/scanner/pkg.yml
new file mode 100644
index 00000000..15c2adbb
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/scanner/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/scanner"
+pkg.type: app
+pkg.description: "Basic scanning application"
+pkg.author: "Krzysztof Kopyściński <krzysztof.kopyscinski@codecoup.pl>"
+
+
+pkg.deps:
+ - "@apache-mynewt-core/kernel/os"
+ - "@apache-mynewt-core/sys/console/full"
+ - "@apache-mynewt-core/sys/log/full"
+ - "@apache-mynewt-core/sys/stats/full"
+ - "@apache-mynewt-core/sys/log/modlog"
+ - "@apache-mynewt-nimble/nimble/host"
+ - "@apache-mynewt-nimble/nimble/host/util/"
+ - "@apache-mynewt-nimble/nimble/host/store/config"
+ - "@apache-mynewt-nimble/nimble/transport" \ No newline at end of file
diff --git a/src/libs/mynewt-nimble/apps/scanner/src/main.c b/src/libs/mynewt-nimble/apps/scanner/src/main.c
new file mode 100644
index 00000000..d21bba4b
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/scanner/src/main.c
@@ -0,0 +1,261 @@
+/*
+ * 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 "sysinit/sysinit.h"
+#include "os/os.h"
+#include "console/console.h"
+#include "host/ble_hs.h"
+#include "host/util/util.h"
+#include "console/console.h"
+#include "log/log.h"
+
+/* scan_event() calls scan(), so forward declaration is required */
+static void scan(void);
+
+static void
+ble_app_set_addr(void)
+{
+ ble_addr_t addr;
+ int rc;
+
+ /* generate new non-resolvable private address */
+ rc = ble_hs_id_gen_rnd(1, &addr);
+ assert(rc == 0);
+
+ /* set generated address */
+ rc = ble_hs_id_set_rnd(addr.val);
+ assert(rc == 0);
+}
+
+static void
+print_uuid(const ble_uuid_t *uuid)
+{
+ char buf[BLE_UUID_STR_LEN];
+
+ MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf));
+}
+
+/* Utility function to log an array of bytes. */
+static 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]);
+ }
+}
+
+static 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;
+}
+
+static 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");
+ }
+}
+
+static int
+scan_event(struct ble_gap_event *event, void *arg)
+{
+ struct ble_hs_adv_fields fields;
+ int rc;
+ switch (event->type) {
+ /* advertising report has been received during discovery procedure */
+ case BLE_GAP_EVENT_DISC:
+ MODLOG_DFLT(ERROR, "Advertising report received!\n");
+ rc = ble_hs_adv_parse_fields(&fields, event->disc.data,
+ event->disc.length_data);
+ if (rc != 0) {
+ return 0;
+ }
+ print_adv_fields(&fields);
+ return 0;
+ /* discovery procedure has terminated */
+ case BLE_GAP_EVENT_DISC_COMPLETE:
+ MODLOG_DFLT(INFO, "Discovery completed, terminaton code: %d\n",
+ event->disc_complete.reason);
+ scan();
+ return 0;
+ default:
+ MODLOG_DFLT(ERROR, "Discovery event not handled\n");
+ return 0;
+ }
+}
+
+static void
+scan(void)
+{
+ /* set scan parameters */
+ struct ble_gap_disc_params scan_params;
+ scan_params.itvl = 500;
+ scan_params.window = 250;
+ scan_params.filter_policy = 0;
+ scan_params.limited = 0;
+ scan_params.passive = 1;
+ scan_params.filter_duplicates = 1;
+ /* performs discovery procedure; value of own_addr_type is hard-coded,
+ because NRPA is used */
+ ble_gap_disc(BLE_OWN_ADDR_RANDOM, 1000, &scan_params, scan_event, NULL);
+}
+
+static void
+on_sync(void)
+{
+ /* Generate a non-resolvable private address. */
+ ble_app_set_addr();
+
+ /* begin scanning */
+ scan();
+}
+
+static void
+on_reset(int reason)
+{
+ console_printf("Resetting state; reason=%d\n", reason);
+}
+
+int
+main(int argc, char **argv)
+{
+ /* Initialize all packages. */
+ sysinit();
+
+ ble_hs_cfg.sync_cb = on_sync;
+ ble_hs_cfg.reset_cb = on_reset;
+
+ /* As the last thing, process events from default event queue. */
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+
+ return 0;
+}