Initial commit for the "body sensor network" code changes.
Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/631cc3d9 Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/631cc3d9 Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/631cc3d9 Branch: refs/heads/bsnbranch Commit: 631cc3d93483079283f1f9094866b1123063a4ec Parents: 63546ef Author: William San Filippo <[email protected]> Authored: Tue Mar 21 13:56:57 2017 -0700 Committer: William San Filippo <[email protected]> Committed: Tue Mar 21 13:56:57 2017 -0700 ---------------------------------------------------------------------- apps/bsncent/pkg.yml | 35 + apps/bsncent/src/bsncent.h | 126 +++ apps/bsncent/src/main.c | 459 +++++++++++ apps/bsncent/src/misc.c | 209 +++++ apps/bsncent/src/peer.c | 812 +++++++++++++++++++ apps/bsncent/syscfg.yml | 26 + apps/bsnprph/pkg.yml | 42 + apps/bsnprph/src/bleprph.h | 60 ++ apps/bsnprph/src/bsnprph.h | 62 ++ apps/bsnprph/src/gatt_svr.c | 131 +++ apps/bsnprph/src/main.c | 378 +++++++++ apps/bsnprph/src/misc.c | 43 + apps/bsnprph/syscfg.yml | 35 + .../controller/include/controller/ble_ll_conn.h | 4 + .../include/controller/ble_ll_sched.h | 34 + net/nimble/controller/src/ble_ll.c | 3 + net/nimble/controller/src/ble_ll_conn.c | 31 +- net/nimble/controller/src/ble_ll_sched.c | 173 ++++ net/nimble/controller/syscfg.yml | 21 + 19 files changed, 2683 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsncent/pkg.yml ---------------------------------------------------------------------- diff --git a/apps/bsncent/pkg.yml b/apps/bsncent/pkg.yml new file mode 100644 index 0000000..6ce9462 --- /dev/null +++ b/apps/bsncent/pkg.yml @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/bsncent +pkg.type: app +pkg.description: Simple BLE central application. +pkg.author: "Apache Mynewt <[email protected]>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - kernel/os + - net/nimble/controller + - net/nimble/host + - net/nimble/host/services/gap + - net/nimble/host/services/gatt + - net/nimble/host/store/ram + - net/nimble/transport/ram + - sys/console/full + - sys/log/full + - sys/stats/full http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsncent/src/bsncent.h ---------------------------------------------------------------------- diff --git a/apps/bsncent/src/bsncent.h b/apps/bsncent/src/bsncent.h new file mode 100644 index 0000000..d15a31c --- /dev/null +++ b/apps/bsncent/src/bsncent.h @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BSNCENT_ +#define H_BSNCENT_ + +#include "os/queue.h" +#include "log/log.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_adv_fields; +struct ble_gap_conn_desc; +struct ble_hs_cfg; +union ble_store_value; +union ble_store_key; + +extern struct log bsncent_log; + +/* bsncent uses the first "peruser" log module. */ +#define BSNCENT_LOG_MODULE (LOG_MODULE_PERUSER + 0) + +/* Convenience macro for logging to the bsncent module. */ +#define BSNCENT_LOG(lvl, ...) \ + LOG_ ## lvl(&bsncent_log, BSNCENT_LOG_MODULE, __VA_ARGS__) + +#define BSNCENT_SVC_GENDATA 0x1811 +#define BSNCENT_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47 +#define BSNCENT_CHR_NEW_ALERT 0x2A46 +#define BSNCENT_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48 +#define BSNCENT_CHR_UNR_ALERT_STAT_UUID 0x2A45 +#define BSNCENT_CHR_ALERT_NOT_CTRL_PT 0x2A44 + +/** GATT server. */ +void gatt_svr_register(void); +void gatt_svr_init_cfg(struct ble_hs_cfg *cfg); + + +/** Misc. */ +void print_bytes(const uint8_t *bytes, int len); +void print_mbuf(const struct os_mbuf *om); +char *addr_str(const void *addr); +void print_uuid(const ble_uuid_t *uuid); +void print_conn_desc(const struct ble_gap_conn_desc *desc); +void print_adv_fields(const struct ble_hs_adv_fields *fields); + +/** Peer. */ +struct peer_dsc { + SLIST_ENTRY(peer_dsc) next; + struct ble_gatt_dsc dsc; +}; +SLIST_HEAD(peer_dsc_list, peer_dsc); + +struct peer_chr { + SLIST_ENTRY(peer_chr) next; + struct ble_gatt_chr chr; + + struct peer_dsc_list dscs; +}; +SLIST_HEAD(peer_chr_list, peer_chr); + +struct peer_svc { + SLIST_ENTRY(peer_svc) next; + struct ble_gatt_svc svc; + + struct peer_chr_list chrs; +}; +SLIST_HEAD(peer_svc_list, peer_svc); + +struct peer; +typedef void peer_disc_fn(const struct peer *peer, int status, void *arg); + +struct peer { + SLIST_ENTRY(peer) next; + + uint16_t conn_handle; + + /** List of discovered GATT services. */ + struct peer_svc_list svcs; + + /** Keeps track of where we are in the service discovery process. */ + uint16_t disc_prev_chr_val; + struct peer_svc *cur_svc; + + /** Callback that gets executed when service discovery completes. */ + peer_disc_fn *disc_cb; + void *disc_cb_arg; +}; + +int peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb, + void *disc_cb_arg); +const struct peer_dsc * +peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid); +const struct peer_chr * +peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid); +const struct peer_svc * +peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid); +int peer_delete(uint16_t conn_handle); +int peer_add(uint16_t conn_handle); +int peer_count(void); +int peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs); + +#ifdef __cplusplus +} +#endif + +#endif http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsncent/src/main.c ---------------------------------------------------------------------- diff --git a/apps/bsncent/src/main.c b/apps/bsncent/src/main.c new file mode 100755 index 0000000..ed89946 --- /dev/null +++ b/apps/bsncent/src/main.c @@ -0,0 +1,459 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <assert.h> +#include <string.h> +#include "syscfg/syscfg.h" +#include "sysinit/sysinit.h" +#include "bsp/bsp.h" +#include "os/os.h" + +/* BLE */ +#include "nimble/ble.h" +#include "controller/ble_ll.h" +#include "host/ble_hs.h" + +/* RAM HCI transport. */ +#include "transport/ram/ble_hci_ram.h" + +/* Mandatory services. */ +#include "services/gap/ble_svc_gap.h" +#include "services/gatt/ble_svc_gatt.h" + +/* Application-specified header. */ +#include "bsncent.h" + +#define BSNCENT_PRINT_RATE (OS_TICKS_PER_SEC * 10) + +struct log bsncent_log; + +static uint32_t num_notify_pkts_rx; +static uint32_t num_notify_bytes_rx; + +/* Prints statistics every 10 seconds. */ +static struct os_callout bsncent_print_timer; + +/* c66f3301-33b3-4687-850a-d52b0d5d1e3c */ +static const ble_uuid128_t bsncent_svc_gendata_uuid = + BLE_UUID128_INIT(0x3c, 0x1e, 0x5d, 0x0d, 0x2b, 0xd5, 0x0a, 0x85, + 0x87, 0x46, 0xb3, 0x33, 0x01, 0x33, 0x6f, 0xc6); + +/* c66f3301-33b3-4687-850a-d52b0d5d1e3d */ +static const ble_uuid128_t bsncent_chr_gendata_uuid = + BLE_UUID128_INIT(0x3d, 0x1e, 0x5d, 0x0d, 0x2b, 0xd5, 0x0a, 0x85, + 0x87, 0x46, 0xb3, 0x33, 0x01, 0x33, 0x6f, 0xc6); + +static const uint8_t bsncent_cent_public_addr[6] = { + 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x00, +}; + +static const ble_addr_t bsncent_peer_addrs[] = { + { BLE_ADDR_PUBLIC, { 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x01 } }, + { BLE_ADDR_PUBLIC, { 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x02 } }, + { BLE_ADDR_PUBLIC, { 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x03 } }, + { BLE_ADDR_PUBLIC, { 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x04 } }, + { BLE_ADDR_PUBLIC, { 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x05 } }, +}; +static const int bsncent_num_peer_addrs = + sizeof bsncent_peer_addrs / sizeof bsncent_peer_addrs[0]; + +static int bsncent_gap_event(struct ble_gap_event *event, void *arg); + +static const struct ble_gap_conn_params ble_gap_conn_params_bsn = { + .scan_itvl = 0x0010, + .scan_window = 0x0010, + .itvl_min = 13, + .itvl_max = 13, + .latency = BLE_GAP_INITIAL_CONN_LATENCY, + .supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT, + .min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN, + .max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN, +}; + +/** + * Application callback. Called when the attempt to subscribe to notifications + * for the ANS Unread Alert Status characteristic has completed. + */ +static int +bsncent_on_subscribe(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + BSNCENT_LOG(INFO, "Subscribe complete; status=%d conn_handle=%d " + "attr_handle=%d\n", + error->status, conn_handle, attr->handle); + + return 0; +} + +/** + * Performs three concurrent GATT operations against the specified peer: + * 1. Reads the ANS Supported New Alert Category characteristic. + * 2. Writes the ANS Alert Notification Control Point characteristic. + * 3. Subscribes to notifications for the ANS Unread Alert Status + * characteristic. + * + * If the peer does not support a required service, characteristic, or + * descriptor, then the peer lied when it claimed support for the alert + * notification service! When this happens, or if a GATT procedure fails, + * this function immediately terminates the connection. + */ +static void +bsncent_subscribe(const struct peer *peer) +{ + const struct peer_dsc *dsc; + uint8_t value[2]; + int rc; + + /* Subscribe to notifications for the gendata characteristic. + * A central enables notifications by writing two bytes (1, 0) to the + * characteristic's client-characteristic-configuration-descriptor (CCCD). + */ + dsc = peer_dsc_find_uuid( + peer, + &bsncent_svc_gendata_uuid.u, + &bsncent_chr_gendata_uuid.u, + BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16)); + if (dsc == NULL) { + BSNCENT_LOG(ERROR, "Error: Peer lacks a CCCD for the generic data " + "characteristic\n"); + goto err; + } + + value[0] = 1; + value[1] = 0; + rc = ble_gattc_write_flat(peer->conn_handle, dsc->dsc.handle, + value, sizeof value, bsncent_on_subscribe, NULL); + if (rc != 0) { + BSNCENT_LOG(ERROR, "Error: Failed to subscribe to characteristic; " + "rc=%d\n", rc); + goto err; + } + + return; + +err: + /* Terminate the connection. */ + ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); +} + +/** + * Called when service discovery of the specified peer has completed. + */ +static void +bsncent_on_disc_complete(const struct peer *peer, int status, void *arg) +{ + + if (status != 0) { + /* Service discovery failed. Terminate the connection. */ + BSNCENT_LOG(ERROR, "Error: Service discovery failed; status=%d " + "conn_handle=%d\n", status, peer->conn_handle); + ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return; + } + + /* Service discovery has completed successfully. Now we have a complete + * list of services, characteristics, and descriptors that the peer + * supports. + */ + BSNCENT_LOG(ERROR, "Service discovery complete; status=%d " + "conn_handle=%d\n", status, peer->conn_handle); + + /* Now subscribe to the gendata characterustic. */ + bsncent_subscribe(peer); +} + + +static int +bsncent_on_mtu_exchanged(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t mtu, void *arg) +{ + int rc; + + if (error->status != 0) { + BSNCENT_LOG(ERROR, "MTU exchange failed; rc=%d\n", error->status); + ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return 0; + } + + /* Perform service discovery. */ + rc = peer_disc_all(conn_handle, bsncent_on_disc_complete, NULL); + if (rc != 0) { + BSNCENT_LOG(ERROR, "Failed to discover services; rc=%d\n", rc); + ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return 0; + } + + return 0; +} + +static void +bsncent_connect(void) +{ + int rc; + + rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, + &ble_gap_conn_params_bsn, bsncent_gap_event, NULL); + if (rc != 0) { + BSNCENT_LOG(ERROR, "Error connecting; rc=%d\n", rc); + if (!((rc == BLE_HS_EALREADY) || (rc == BLE_HS_EBUSY))) { + /* Only assert if we are not already trying */ + assert(0); + } + } +} + +static void +bsncent_fill_wl(void) +{ + int rc; + + rc = ble_gap_wl_set(bsncent_peer_addrs, bsncent_num_peer_addrs); + if (rc != 0) { + BSNCENT_LOG(ERROR, "Error setting white list; rc=%d\n", rc); + assert(0); + } +} + +static void +bsncent_print_timer_reset(void) +{ + int rc; + + rc = os_callout_reset(&bsncent_print_timer, BSNCENT_PRINT_RATE); + assert(rc == 0); +} + +/** + * Prints statistics every 10 seconds. + */ +static void +bsncent_print_timer_exp(struct os_event *ev) +{ + static uint32_t prev_bytes; + static uint32_t prev_pkts; + uint32_t diff_bytes; + uint32_t diff_pkts; + + console_printf("--\n"); + console_printf("%8d connections\n", peer_count()); + console_printf("%8lu packets received\n", + (unsigned long)num_notify_pkts_rx); + console_printf("%8lu bytes received\n", + (unsigned long)num_notify_bytes_rx); + + if (prev_pkts != 0) { + diff_pkts = num_notify_pkts_rx - prev_pkts; + diff_bytes = num_notify_bytes_rx - prev_bytes; + + console_printf("%8lu packets per second\n", + (unsigned long)(diff_pkts / + (BSNCENT_PRINT_RATE / + OS_TICKS_PER_SEC))); + console_printf("%8lu bytes per second\n", + (unsigned long)(diff_bytes / + (BSNCENT_PRINT_RATE / + OS_TICKS_PER_SEC))); + } + + prev_pkts = num_notify_pkts_rx; + prev_bytes = num_notify_bytes_rx; + + bsncent_print_timer_reset(); +} + +/** + * The nimble host executes this callback when a GAP event occurs. The + * application associates a GAP event callback with each connection that is + * established. bsncent uses the same callback for all connections. + * + * @param event The event being signalled. + * @param arg Application-specified argument; unused by + * bsncent. + * + * @return 0 if the application successfully handled the + * event; nonzero on failure. The semantics + * of the return code is specific to the + * particular GAP event being signalled. + */ +static int +bsncent_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + /* Connection successfully established. */ + BSNCENT_LOG(INFO, "Connection established "); + + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + BSNCENT_LOG(INFO, "\n"); + + /* Remember peer. */ + rc = peer_add(event->connect.conn_handle); + if (rc != 0) { + BSNCENT_LOG(ERROR, "Failed to add peer; rc=%d\n", rc); + assert(0); + } + + /* Try to connect to next peer if any remain unconnected. */ + if (peer_count() < bsncent_num_peer_addrs) { + bsncent_connect(); + } + + /* Negotiate ATT MTU. */ + rc = ble_gattc_exchange_mtu(event->connect.conn_handle, + bsncent_on_mtu_exchanged, NULL); + if (rc != 0) { + BSNCENT_LOG(ERROR, "Failed to exchange MTU; rc=%d\n", rc); + return 0; + } + } else { + /* Connection attempt failed; resume connecting. */ + BSNCENT_LOG(ERROR, "Error: Connection failed; status=%d\n", + event->connect.status); + bsncent_connect(); + } + + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + /* Connection terminated. */ + BSNCENT_LOG(INFO, "disconnect; reason=%d ", event->disconnect.reason); + print_conn_desc(&event->disconnect.conn); + BSNCENT_LOG(INFO, "\n"); + + /* Forget about peer. */ + peer_delete(event->disconnect.conn.conn_handle); + + /* Resume scanning. */ + bsncent_connect(); + return 0; + + case BLE_GAP_EVENT_ENC_CHANGE: + /* Encryption has been enabled or disabled for this connection. */ + BSNCENT_LOG(INFO, "encryption change event; status=%d ", + event->enc_change.status); + rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + return 0; + + case BLE_GAP_EVENT_NOTIFY_RX: + /* Peer sent us a notification or indication. */ + BSNCENT_LOG(DEBUG, "received %s; conn_handle=%d attr_handle=%d " + "attr_len=%d\n", + event->notify_rx.indication ? + "indication" : + "notification", + event->notify_rx.conn_handle, + event->notify_rx.attr_handle, + OS_MBUF_PKTLEN(event->notify_rx.om)); + + num_notify_pkts_rx++; + num_notify_bytes_rx += OS_MBUF_PKTLEN(event->notify_rx.om); + + /* Attribute data is contained in event->notify_rx.attr_data. */ + return 0; + + case BLE_GAP_EVENT_MTU: + BSNCENT_LOG(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.channel_id, + event->mtu.value); + return 0; + + default: + return 0; + } +} + +static void +bsncent_on_reset(int reason) +{ + BSNCENT_LOG(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +bsncent_on_sync(void) +{ + /* Start printing statistics. */ + bsncent_print_timer_reset(); + + /* Add the five known peripherals to the white list. */ + bsncent_fill_wl(); + + /* Attempt to connect to the first peripheral. */ + bsncent_connect(); +} + +/** + * main + * + * All application logic and NimBLE host work is performed in default task. + * + * @return int NOTE: this function should never return! + */ +int +main(void) +{ + int rc; + + /* Set initial BLE device address. */ + memcpy(g_dev_addr, bsncent_cent_public_addr, 6); + + /* Initialize OS */ + sysinit(); + + /* Initialize the bsncent log. */ + log_register("bsncent", &bsncent_log, &log_console_handler, NULL, + LOG_SYSLEVEL); + + /* Configure the host. */ + log_register("ble_hs", &ble_hs_log, &log_console_handler, NULL, + LOG_SYSLEVEL); + ble_hs_cfg.reset_cb = bsncent_on_reset; + ble_hs_cfg.sync_cb = bsncent_on_sync; + + os_callout_init(&bsncent_print_timer, os_eventq_dflt_get(), + bsncent_print_timer_exp, NULL); + + /* XXX: I think some of these need to be based on # of connections */ + /* Initialize data structures to track connected peers. */ + rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 96, 64); + assert(rc == 0); + + /* Set the default device name. */ + rc = ble_svc_gap_device_name_set(MYNEWT_VAL(BSNCENT_BLE_NAME)); + assert(rc == 0); + + /* os start should never return. If it does, this should be an error */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsncent/src/misc.c ---------------------------------------------------------------------- diff --git a/apps/bsncent/src/misc.c b/apps/bsncent/src/misc.c new file mode 100644 index 0000000..910a304 --- /dev/null +++ b/apps/bsncent/src/misc.c @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "bsncent.h" + +/** + * Utility function to log an array of bytes. + */ +void +print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + BSNCENT_LOG(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +void +print_mbuf(const struct os_mbuf *om) +{ + int colon; + + colon = 0; + while (om != NULL) { + if (colon) { + BSNCENT_LOG(DEBUG, ":"); + } else { + colon = 1; + } + print_bytes(om->om_data, om->om_len); + om = SLIST_NEXT(om, om_next); + } +} + +char * +addr_str(const void *addr) +{ + static char buf[6 * 2 + 5 + 1]; + const uint8_t *u8p; + + u8p = addr; + sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); + + return buf; +} + +void +print_uuid(const ble_uuid_t *uuid) +{ + char buf[BLE_UUID_STR_LEN]; + + BSNCENT_LOG(DEBUG, "%s", ble_uuid_to_str(uuid, buf)); +} + +/** + * Logs information about a connection to the console. + */ +void +print_conn_desc(const struct ble_gap_conn_desc *desc) +{ + BSNCENT_LOG(DEBUG, "handle=%d our_ota_addr_type=%d our_ota_addr=%s ", + desc->conn_handle, desc->our_ota_addr.type, + addr_str(desc->our_ota_addr.val)); + BSNCENT_LOG(DEBUG, "our_id_addr_type=%d our_id_addr=%s ", + desc->our_id_addr.type, addr_str(desc->our_id_addr.val)); + BSNCENT_LOG(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ", + desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val)); + BSNCENT_LOG(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ", + desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val)); + BSNCENT_LOG(DEBUG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "encrypted=%d authenticated=%d bonded=%d", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + + +void +print_adv_fields(const struct ble_hs_adv_fields *fields) +{ + char s[BLE_HS_ADV_MAX_SZ]; + const uint8_t *u8p; + int i; + + if (fields->flags != 0) { + BSNCENT_LOG(DEBUG, " flags=0x%02x\n", fields->flags); + } + + if (fields->uuids16 != NULL) { + BSNCENT_LOG(DEBUG, " uuids16(%scomplete)=", + fields->uuids16_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids16; i++) { + print_uuid(&fields->uuids16[i].u); + BSNCENT_LOG(DEBUG, " "); + } + BSNCENT_LOG(DEBUG, "\n"); + } + + if (fields->uuids32 != NULL) { + BSNCENT_LOG(DEBUG, " uuids32(%scomplete)=", + fields->uuids32_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids32; i++) { + print_uuid(&fields->uuids32[i].u); + BSNCENT_LOG(DEBUG, " "); + } + BSNCENT_LOG(DEBUG, "\n"); + } + + if (fields->uuids128 != NULL) { + BSNCENT_LOG(DEBUG, " uuids128(%scomplete)=", + fields->uuids128_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids128; i++) { + print_uuid(&fields->uuids128[i].u); + BSNCENT_LOG(DEBUG, " "); + } + BSNCENT_LOG(DEBUG, "\n"); + } + + if (fields->name != NULL) { + assert(fields->name_len < sizeof s - 1); + memcpy(s, fields->name, fields->name_len); + s[fields->name_len] = '\0'; + BSNCENT_LOG(DEBUG, " name(%scomplete)=%s\n", + fields->name_is_complete ? "" : "in", s); + } + + if (fields->tx_pwr_lvl_is_present) { + BSNCENT_LOG(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl); + } + + if (fields->slave_itvl_range != NULL) { + BSNCENT_LOG(DEBUG, " slave_itvl_range="); + print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + BSNCENT_LOG(DEBUG, "\n"); + } + + if (fields->svc_data_uuid16 != NULL) { + BSNCENT_LOG(DEBUG, " svc_data_uuid16="); + print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len); + BSNCENT_LOG(DEBUG, "\n"); + } + + if (fields->public_tgt_addr != NULL) { + BSNCENT_LOG(DEBUG, " public_tgt_addr="); + u8p = fields->public_tgt_addr; + for (i = 0; i < fields->num_public_tgt_addrs; i++) { + BSNCENT_LOG(DEBUG, "public_tgt_addr=%s ", addr_str(u8p)); + u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; + } + BSNCENT_LOG(DEBUG, "\n"); + } + + if (fields->appearance_is_present) { + BSNCENT_LOG(DEBUG, " appearance=0x%04x\n", fields->appearance); + } + + if (fields->adv_itvl_is_present) { + BSNCENT_LOG(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl); + } + + if (fields->svc_data_uuid32 != NULL) { + BSNCENT_LOG(DEBUG, " svc_data_uuid32="); + print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len); + BSNCENT_LOG(DEBUG, "\n"); + } + + if (fields->svc_data_uuid128 != NULL) { + BSNCENT_LOG(DEBUG, " svc_data_uuid128="); + print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len); + BSNCENT_LOG(DEBUG, "\n"); + } + + if (fields->uri != NULL) { + BSNCENT_LOG(DEBUG, " uri="); + print_bytes(fields->uri, fields->uri_len); + BSNCENT_LOG(DEBUG, "\n"); + } + + if (fields->mfg_data != NULL) { + BSNCENT_LOG(DEBUG, " mfg_data="); + print_bytes(fields->mfg_data, fields->mfg_data_len); + BSNCENT_LOG(DEBUG, "\n"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsncent/src/peer.c ---------------------------------------------------------------------- diff --git a/apps/bsncent/src/peer.c b/apps/bsncent/src/peer.c new file mode 100644 index 0000000..0e4f7f4 --- /dev/null +++ b/apps/bsncent/src/peer.c @@ -0,0 +1,812 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <assert.h> +#include <string.h> +#include "host/ble_hs.h" +#include "bsncent.h" + +static void *peer_svc_mem; +static struct os_mempool peer_svc_pool; + +static void *peer_chr_mem; +static struct os_mempool peer_chr_pool; + +static void *peer_dsc_mem; +static struct os_mempool peer_dsc_pool; + +static void *peer_mem; +static struct os_mempool peer_pool; +static SLIST_HEAD(, peer) peers; + +static struct peer_svc * +peer_svc_find_range(struct peer *peer, uint16_t attr_handle); +static struct peer_svc * +peer_svc_find(struct peer *peer, uint16_t svc_start_handle, + struct peer_svc **out_prev); +int +peer_svc_is_empty(const struct peer_svc *svc); + +uint16_t +chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr); +int +chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr); +static struct peer_chr * +peer_chr_find(const struct peer_svc *svc, uint16_t chr_def_handle, + struct peer_chr **out_prev); +static void +peer_disc_chrs(struct peer *peer); + +static int +peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + uint16_t chr_def_handle, const struct ble_gatt_dsc *dsc, + void *arg); + +static struct peer * +peer_find(uint16_t conn_handle) +{ + struct peer *peer; + + SLIST_FOREACH(peer, &peers, next) { + if (peer->conn_handle == conn_handle) { + return peer; + } + } + + return NULL; +} + +static void +peer_disc_complete(struct peer *peer, int rc) +{ + peer->disc_prev_chr_val = 0; + + /* Notify caller that discovery has completed. */ + if (peer->disc_cb != NULL) { + peer->disc_cb(peer, rc, peer->disc_cb_arg); + } +} + +static struct peer_dsc * +peer_dsc_find_prev(const struct peer_chr *chr, uint16_t dsc_handle) +{ + struct peer_dsc *prev; + struct peer_dsc *dsc; + + prev = NULL; + SLIST_FOREACH(dsc, &chr->dscs, next) { + if (dsc->dsc.handle >= dsc_handle) { + break; + } + + prev = dsc; + } + + return prev; +} + +static struct peer_dsc * +peer_dsc_find(const struct peer_chr *chr, uint16_t dsc_handle, + struct peer_dsc **out_prev) +{ + struct peer_dsc *prev; + struct peer_dsc *dsc; + + prev = peer_dsc_find_prev(chr, dsc_handle); + if (prev == NULL) { + dsc = SLIST_FIRST(&chr->dscs); + } else { + dsc = SLIST_NEXT(prev, next); + } + + if (dsc != NULL && dsc->dsc.handle != dsc_handle) { + dsc = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return dsc; +} + +static int +peer_dsc_add(struct peer *peer, uint16_t chr_val_handle, + const struct ble_gatt_dsc *gatt_dsc) +{ + struct peer_dsc *prev; + struct peer_dsc *dsc; + struct peer_svc *svc; + struct peer_chr *chr; + + svc = peer_svc_find_range(peer, chr_val_handle); + if (svc == NULL) { + /* Can't find service for discovered descriptor; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + chr = peer_chr_find(svc, chr_val_handle, NULL); + if (chr == NULL) { + /* Can't find characteristic for discovered descriptor; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + dsc = peer_dsc_find(chr, gatt_dsc->handle, &prev); + if (dsc != NULL) { + /* Descriptor already discovered. */ + return 0; + } + + dsc = os_memblock_get(&peer_dsc_pool); + if (dsc == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(dsc, 0, sizeof *dsc); + + dsc->dsc = *gatt_dsc; + + if (prev == NULL) { + SLIST_INSERT_HEAD(&chr->dscs, dsc, next); + } else { + SLIST_NEXT(prev, next) = dsc; + } + + return 0; +} + +static void +peer_disc_dscs(struct peer *peer) +{ + struct peer_chr *chr; + struct peer_svc *svc; + int rc; + + /* Search through the list of discovered characteristics for the first + * characteristic that contains undiscovered descriptors. Then, discover + * all descriptors belonging to that characteristic. + */ + SLIST_FOREACH(svc, &peer->svcs, next) { + SLIST_FOREACH(chr, &svc->chrs, next) { + if (!chr_is_empty(svc, chr) && + SLIST_EMPTY(&chr->dscs) && + peer->disc_prev_chr_val <= chr->chr.def_handle) { + + rc = ble_gattc_disc_all_dscs(peer->conn_handle, + chr->chr.val_handle, + chr_end_handle(svc, chr), + peer_dsc_disced, peer); + if (rc != 0) { + peer_disc_complete(peer, rc); + } + + peer->disc_prev_chr_val = chr->chr.val_handle; + return; + } + } + } + + /* All descriptors discovered. */ + peer_disc_complete(peer, 0); +} + +static int +peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, + void *arg) +{ + struct peer *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = peer_dsc_add(peer, chr_val_handle, dsc); + break; + + case BLE_HS_EDONE: + /* All descriptors in this characteristic discovered; start discovering + * descriptors in the next characteristic. + */ + if (peer->disc_prev_chr_val > 0) { + peer_disc_dscs(peer); + } + rc = 0; + break; + + default: + /* Error; abort discovery. */ + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + peer_disc_complete(peer, rc); + } + + return rc; +} + +uint16_t +chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr) +{ + const struct peer_chr *next_chr; + + next_chr = SLIST_NEXT(chr, next); + if (next_chr != NULL) { + return next_chr->chr.def_handle - 1; + } else { + return svc->svc.end_handle; + } +} + +int +chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr) +{ + return chr_end_handle(svc, chr) <= chr->chr.val_handle; +} + +static struct peer_chr * +peer_chr_find_prev(const struct peer_svc *svc, uint16_t chr_val_handle) +{ + struct peer_chr *prev; + struct peer_chr *chr; + + prev = NULL; + SLIST_FOREACH(chr, &svc->chrs, next) { + if (chr->chr.val_handle >= chr_val_handle) { + break; + } + + prev = chr; + } + + return prev; +} + +static struct peer_chr * +peer_chr_find(const struct peer_svc *svc, uint16_t chr_val_handle, + struct peer_chr **out_prev) +{ + struct peer_chr *prev; + struct peer_chr *chr; + + prev = peer_chr_find_prev(svc, chr_val_handle); + if (prev == NULL) { + chr = SLIST_FIRST(&svc->chrs); + } else { + chr = SLIST_NEXT(prev, next); + } + + if (chr != NULL && chr->chr.val_handle != chr_val_handle) { + chr = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return chr; +} + +static void +peer_chr_delete(struct peer_chr *chr) +{ + struct peer_dsc *dsc; + + while ((dsc = SLIST_FIRST(&chr->dscs)) != NULL) { + SLIST_REMOVE_HEAD(&chr->dscs, next); + os_memblock_put(&peer_dsc_pool, dsc); + } + + os_memblock_put(&peer_chr_pool, chr); +} + +static int +peer_chr_add(struct peer *peer, uint16_t svc_start_handle, + const struct ble_gatt_chr *gatt_chr) +{ + struct peer_chr *prev; + struct peer_chr *chr; + struct peer_svc *svc; + + svc = peer_svc_find(peer, svc_start_handle, NULL); + if (svc == NULL) { + /* Can't find service for discovered characteristic; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + chr = peer_chr_find(svc, gatt_chr->def_handle, &prev); + if (chr != NULL) { + /* Characteristic already discovered. */ + return 0; + } + + chr = os_memblock_get(&peer_chr_pool); + if (chr == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(chr, 0, sizeof *chr); + + chr->chr = *gatt_chr; + + if (prev == NULL) { + SLIST_INSERT_HEAD(&svc->chrs, chr, next); + } else { + SLIST_NEXT(prev, next) = chr; + } + + return 0; +} + +static int +peer_chr_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) +{ + struct peer *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = peer_chr_add(peer, peer->cur_svc->svc.start_handle, chr); + break; + + case BLE_HS_EDONE: + /* All characteristics in this service discovered; start discovering + * characteristics in the next service. + */ + if (peer->disc_prev_chr_val > 0) { + peer_disc_chrs(peer); + } + rc = 0; + break; + + default: + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + peer_disc_complete(peer, rc); + } + + return rc; +} + +static void +peer_disc_chrs(struct peer *peer) +{ + struct peer_svc *svc; + int rc; + + /* Search through the list of discovered service for the first service that + * contains undiscovered characteristics. Then, discover all + * characteristics belonging to that service. + */ + SLIST_FOREACH(svc, &peer->svcs, next) { + if (!peer_svc_is_empty(svc) && SLIST_EMPTY(&svc->chrs)) { + peer->cur_svc = svc; + rc = ble_gattc_disc_all_chrs(peer->conn_handle, + svc->svc.start_handle, + svc->svc.end_handle, + peer_chr_disced, peer); + if (rc != 0) { + peer_disc_complete(peer, rc); + } + return; + } + } + + /* All characteristics discovered. */ + peer_disc_dscs(peer); +} + +int +peer_svc_is_empty(const struct peer_svc *svc) +{ + return svc->svc.end_handle < svc->svc.start_handle; +} + +static struct peer_svc * +peer_svc_find_prev(struct peer *peer, uint16_t svc_start_handle) +{ + struct peer_svc *prev; + struct peer_svc *svc; + + prev = NULL; + SLIST_FOREACH(svc, &peer->svcs, next) { + if (svc->svc.start_handle >= svc_start_handle) { + break; + } + + prev = svc; + } + + return prev; +} + +static struct peer_svc * +peer_svc_find(struct peer *peer, uint16_t svc_start_handle, + struct peer_svc **out_prev) +{ + struct peer_svc *prev; + struct peer_svc *svc; + + prev = peer_svc_find_prev(peer, svc_start_handle); + if (prev == NULL) { + svc = SLIST_FIRST(&peer->svcs); + } else { + svc = SLIST_NEXT(prev, next); + } + + if (svc != NULL && svc->svc.start_handle != svc_start_handle) { + svc = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return svc; +} + +static struct peer_svc * +peer_svc_find_range(struct peer *peer, uint16_t attr_handle) +{ + struct peer_svc *svc; + + SLIST_FOREACH(svc, &peer->svcs, next) { + if (svc->svc.start_handle <= attr_handle && + svc->svc.end_handle >= attr_handle) { + + return svc; + } + } + + return NULL; +} + +const struct peer_svc * +peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid) +{ + const struct peer_svc *svc; + + SLIST_FOREACH(svc, &peer->svcs, next) { + if (ble_uuid_cmp(&svc->svc.uuid.u, uuid) == 0) { + return svc; + } + } + + return NULL; +} + +const struct peer_chr * +peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid) +{ + const struct peer_svc *svc; + const struct peer_chr *chr; + + svc = peer_svc_find_uuid(peer, svc_uuid); + if (svc == NULL) { + return NULL; + } + + SLIST_FOREACH(chr, &svc->chrs, next) { + if (ble_uuid_cmp(&chr->chr.uuid.u, chr_uuid) == 0) { + return chr; + } + } + + return NULL; +} + +const struct peer_dsc * +peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid) +{ + const struct peer_chr *chr; + const struct peer_dsc *dsc; + + chr = peer_chr_find_uuid(peer, svc_uuid, chr_uuid); + if (chr == NULL) { + return NULL; + } + + SLIST_FOREACH(dsc, &chr->dscs, next) { + if (ble_uuid_cmp(&dsc->dsc.uuid.u, dsc_uuid) == 0) { + return dsc; + } + } + + return NULL; +} + +static int +peer_svc_add(struct peer *peer, const struct ble_gatt_svc *gatt_svc) +{ + struct peer_svc *prev; + struct peer_svc *svc; + + svc = peer_svc_find(peer, gatt_svc->start_handle, &prev); + if (svc != NULL) { + /* Service already discovered. */ + return 0; + } + + svc = os_memblock_get(&peer_svc_pool); + if (svc == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(svc, 0, sizeof *svc); + + svc->svc = *gatt_svc; + SLIST_INIT(&svc->chrs); + + if (prev == NULL) { + SLIST_INSERT_HEAD(&peer->svcs, svc, next); + } else { + SLIST_INSERT_AFTER(prev, svc, next); + } + + return 0; +} + +static void +peer_svc_delete(struct peer_svc *svc) +{ + struct peer_chr *chr; + + while ((chr = SLIST_FIRST(&svc->chrs)) != NULL) { + SLIST_REMOVE_HEAD(&svc->chrs, next); + peer_chr_delete(chr); + } + + os_memblock_put(&peer_svc_pool, svc); +} + +static int +peer_svc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, void *arg) +{ + struct peer *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = peer_svc_add(peer, service); + break; + + case BLE_HS_EDONE: + /* All services discovered; start discovering characteristics. */ + if (peer->disc_prev_chr_val > 0) { + peer_disc_chrs(peer); + } + rc = 0; + break; + + default: + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + peer_disc_complete(peer, rc); + } + + return rc; +} + +int +peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb, void *disc_cb_arg) +{ + struct peer_svc *svc; + struct peer *peer; + int rc; + + peer = peer_find(conn_handle); + if (peer == NULL) { + return BLE_HS_ENOTCONN; + } + + /* Undiscover everything first. */ + while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) { + SLIST_REMOVE_HEAD(&peer->svcs, next); + peer_svc_delete(svc); + } + + peer->disc_prev_chr_val = 1; + peer->disc_cb = disc_cb; + peer->disc_cb_arg = disc_cb_arg; + + rc = ble_gattc_disc_all_svcs(conn_handle, peer_svc_disced, peer); + if (rc != 0) { + return rc; + } + + return 0; +} + +int +peer_delete(uint16_t conn_handle) +{ + struct peer_svc *svc; + struct peer *peer; + int rc; + + peer = peer_find(conn_handle); + if (peer == NULL) { + return BLE_HS_ENOTCONN; + } + + SLIST_REMOVE(&peers, peer, peer, next); + + while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) { + SLIST_REMOVE_HEAD(&peer->svcs, next); + peer_svc_delete(svc); + } + + rc = os_memblock_put(&peer_pool, peer); + if (rc != 0) { + return BLE_HS_EOS; + } + + return 0; +} + +int +peer_add(uint16_t conn_handle) +{ + struct peer *peer; + + /* Make sure the connection handle is unique. */ + peer = peer_find(conn_handle); + if (peer != NULL) { + return BLE_HS_EALREADY; + } + + peer = os_memblock_get(&peer_pool); + if (peer == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + + memset(peer, 0, sizeof *peer); + peer->conn_handle = conn_handle; + + SLIST_INSERT_HEAD(&peers, peer, next); + + return 0; +} + +int +peer_count(void) +{ + return peer_pool.mp_num_blocks - peer_pool.mp_num_free; +} + +static void +peer_free_mem(void) +{ + free(peer_mem); + peer_mem = NULL; + + free(peer_svc_mem); + peer_svc_mem = NULL; + + free(peer_chr_mem); + peer_chr_mem = NULL; + + free(peer_dsc_mem); + peer_dsc_mem = NULL; +} + +int +peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs) +{ + int rc; + + /* Free memory first in case this function gets called more than once. */ + peer_free_mem(); + + peer_mem = malloc( + OS_MEMPOOL_BYTES(max_peers, sizeof (struct peer))); + if (peer_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_pool, max_peers, + sizeof (struct peer), peer_mem, + "peer_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + peer_svc_mem = malloc( + OS_MEMPOOL_BYTES(max_svcs, sizeof (struct peer_svc))); + if (peer_svc_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_svc_pool, max_svcs, + sizeof (struct peer_svc), peer_svc_mem, + "peer_svc_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + peer_chr_mem = malloc( + OS_MEMPOOL_BYTES(max_chrs, sizeof (struct peer_chr))); + if (peer_chr_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_chr_pool, max_chrs, + sizeof (struct peer_chr), peer_chr_mem, + "peer_chr_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + peer_dsc_mem = malloc( + OS_MEMPOOL_BYTES(max_dscs, sizeof (struct peer_dsc))); + if (peer_dsc_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_dsc_pool, max_dscs, + sizeof (struct peer_dsc), peer_dsc_mem, + "peer_dsc_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + return 0; + +err: + peer_free_mem(); + return rc; +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsncent/syscfg.yml ---------------------------------------------------------------------- diff --git a/apps/bsncent/syscfg.yml b/apps/bsncent/syscfg.yml new file mode 100644 index 0000000..dc8633d --- /dev/null +++ b/apps/bsncent/syscfg.yml @@ -0,0 +1,26 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + BSNCENT_BLE_NAME: + description: The BLE name to use. + value: '"bsncent"' + +syscfg.vals: + # Disable logging. + LOG_LEVEL: 255 http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/pkg.yml ---------------------------------------------------------------------- diff --git a/apps/bsnprph/pkg.yml b/apps/bsnprph/pkg.yml new file mode 100644 index 0000000..2328798 --- /dev/null +++ b/apps/bsnprph/pkg.yml @@ -0,0 +1,42 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/bsnprph +pkg.type: app +pkg.description: Simple BLE peripheral application. +pkg.author: "Apache Mynewt <[email protected]>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - boot/split + - kernel/os + - mgmt/imgmgr + - mgmt/newtmgr + - mgmt/newtmgr/transport/ble + - net/nimble/controller + - net/nimble/host + - net/nimble/host/services/ans + - net/nimble/host/services/gap + - net/nimble/host/services/gatt + - net/nimble/host/store/ram + - net/nimble/transport/ram + - sys/console/full + - sys/log/full + - sys/stats/full + - sys/sysinit + - sys/id http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/src/bleprph.h ---------------------------------------------------------------------- diff --git a/apps/bsnprph/src/bleprph.h b/apps/bsnprph/src/bleprph.h new file mode 100644 index 0000000..afcbb72 --- /dev/null +++ b/apps/bsnprph/src/bleprph.h @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLEPRPH_ +#define H_BLEPRPH_ + +#include "log/log.h" +#include "nimble/ble.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_cfg; +struct ble_gatt_register_ctxt; + +extern struct log bleprph_log; + +/* bleprph uses the first "peruser" log module. */ +#define BLEPRPH_LOG_MODULE (LOG_MODULE_PERUSER + 0) + +/* Convenience macro for logging to the bleprph module. */ +#define BLEPRPH_LOG(lvl, ...) \ + LOG_ ## lvl(&bleprph_log, BLEPRPH_LOG_MODULE, __VA_ARGS__) + +/** GATT server. */ +#define GATT_SVR_SVC_ALERT_UUID 0x1811 +#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47 +#define GATT_SVR_CHR_NEW_ALERT 0x2A46 +#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48 +#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID 0x2A45 +#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT 0x2A44 + +void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); +int gatt_svr_init(void); + +/** Misc. */ +void print_bytes(const uint8_t *bytes, int len); +void print_addr(const void *addr); + +#ifdef __cplusplus +} +#endif + +#endif http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/src/bsnprph.h ---------------------------------------------------------------------- diff --git a/apps/bsnprph/src/bsnprph.h b/apps/bsnprph/src/bsnprph.h new file mode 100644 index 0000000..d8e814f --- /dev/null +++ b/apps/bsnprph/src/bsnprph.h @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BSNPRPH_ +#define H_BSNPRPH_ + +#include "log/log.h" +#include "nimble/ble.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_cfg; +struct ble_gatt_register_ctxt; + +extern struct log bsnprph_log; + +/* bsnprph uses the first "peruser" log module. */ +#define BSNPRPH_LOG_MODULE (LOG_MODULE_PERUSER + 0) + +/* Convenience macro for logging to the bsnprph module. */ +#define BSNPRPH_LOG(lvl, ...) \ + LOG_ ## lvl(&bsnprph_log, BSNPRPH_LOG_MODULE, __VA_ARGS__) + +/** GATT server. */ +#define GATT_SVR_SVC_ALERT_UUID 0x1811 +#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47 +#define GATT_SVR_CHR_NEW_ALERT 0x2A46 +#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48 +#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID 0x2A45 +#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT 0x2A44 + +void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); +int gatt_svr_init(void); + +/** Misc. */ +void print_bytes(const uint8_t *bytes, int len); +void print_addr(const void *addr); + +extern uint16_t gatt_svr_chr_gendata_val_handle; + +#ifdef __cplusplus +} +#endif + +#endif http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/src/gatt_svr.c ---------------------------------------------------------------------- diff --git a/apps/bsnprph/src/gatt_svr.c b/apps/bsnprph/src/gatt_svr.c new file mode 100644 index 0000000..f8c2af9 --- /dev/null +++ b/apps/bsnprph/src/gatt_svr.c @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include "bsp/bsp.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "bsnprph.h" + +/** + * The vendor specific security test service consists of two characteristics: + * o random-number-generator: generates a random 32-bit number each time + * it is read. This characteristic can only be read over an encrypted + * connection. + * o static-value: a single-byte characteristic that can always be read, + * but can only be written over an encrypted connection. + */ + +/* c66f3301-33b3-4687-850a-d52b0d5d1e3c */ +static const ble_uuid128_t gatt_svr_svc_gendata_uuid = + BLE_UUID128_INIT(0x3c, 0x1e, 0x5d, 0x0d, 0x2b, 0xd5, 0x0a, 0x85, + 0x87, 0x46, 0xb3, 0x33, 0x01, 0x33, 0x6f, 0xc6); + +/* c66f3301-33b3-4687-850a-d52b0d5d1e3d */ +static const ble_uuid128_t gatt_svr_chr_gendata_uuid = + BLE_UUID128_INIT(0x3d, 0x1e, 0x5d, 0x0d, 0x2b, 0xd5, 0x0a, 0x85, + 0x87, 0x46, 0xb3, 0x33, 0x01, 0x33, 0x6f, 0xc6); + +static int +gatt_svr_chr_access_gendata(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +uint16_t gatt_svr_chr_gendata_val_handle; + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /*** Service: Generic data. */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = &gatt_svr_svc_gendata_uuid.u, + .characteristics = (struct ble_gatt_chr_def[]) { { + /*** Characteristic: Generic data. */ + .uuid = &gatt_svr_chr_gendata_uuid.u, + .access_cb = gatt_svr_chr_access_gendata, + .flags = BLE_GATT_CHR_F_NOTIFY, + .val_handle = &gatt_svr_chr_gendata_val_handle, + }, { + 0, /* No more characteristics in this service. */ + } }, + }, + + { + 0, /* No more services. */ + }, +}; + +static int +gatt_svr_chr_access_gendata(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + return BLE_ATT_ERR_UNLIKELY; +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + BSNPRPH_LOG(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + BSNPRPH_LOG(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + BSNPRPH_LOG(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +int +gatt_svr_init(void) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + return 0; +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/src/main.c ---------------------------------------------------------------------- diff --git a/apps/bsnprph/src/main.c b/apps/bsnprph/src/main.c new file mode 100755 index 0000000..23e8b5a --- /dev/null +++ b/apps/bsnprph/src/main.c @@ -0,0 +1,378 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <assert.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include "sysinit/sysinit.h" +#include "bsp/bsp.h" +#include "os/os.h" +#include "bsp/bsp.h" +#include "hal/hal_gpio.h" +#include "console/console.h" +#include "hal/hal_system.h" +#include "config/config.h" +#include "split/split.h" + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "services/gap/ble_svc_gap.h" + +/* Application-specified header. */ +#include "bsnprph.h" + +#define BSNPRPH_PKT_SZ 80 +#define BSNPRPH_TX_TIMER_RATE 2 + +static const uint8_t bsnprph_prph_public_addr[6] = { + 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x05, +}; + +static const ble_addr_t bsnprph_central_addr = { + BLE_ADDR_PUBLIC, + { 0x0a, 0x0b, 0x09, 0x09, 0x09, 0x00 }, +}; + +struct log bsnprph_log; + +/* Sends data to central at 60 Hz. */ +static struct os_callout bsnprph_tx_timer; + +/* The handle of the current connection. */ +static uint16_t bsnprph_conn_handle; + +static int bsnprph_gap_event(struct ble_gap_event *event, void *arg); + +/** + * Logs information about a connection to the console. + */ +static void +bsnprph_print_conn_desc(struct ble_gap_conn_desc *desc) +{ + BSNPRPH_LOG(INFO, "handle=%d our_ota_addr_type=%d our_ota_addr=", + desc->conn_handle, desc->our_ota_addr.type); + print_addr(desc->our_ota_addr.val); + BSNPRPH_LOG(INFO, " our_id_addr_type=%d our_id_addr=", + desc->our_id_addr.type); + print_addr(desc->our_id_addr.val); + BSNPRPH_LOG(INFO, " peer_ota_addr_type=%d peer_ota_addr=", + desc->peer_ota_addr.type); + print_addr(desc->peer_ota_addr.val); + BSNPRPH_LOG(INFO, " peer_id_addr_type=%d peer_id_addr=", + desc->peer_id_addr.type); + print_addr(desc->peer_id_addr.val); + BSNPRPH_LOG(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "encrypted=%d authenticated=%d bonded=%d\n", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + +/** + * Enables advertising with the following parameters: + * o General discoverable mode. + * o Directed connectable mode. + */ +static void +bsnprph_advertise(void) +{ + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + const char *name; + int rc; + + /** + * Set the advertisement data included in our advertisements: + * o Flags (indicates advertisement type and other general info). + * o Advertising tx power. + * o Device name. + * o 16-bit service UUIDs (alert notifications). + */ + + memset(&fields, 0, sizeof fields); + + /* Advertise two flags: + * o Discoverability in forthcoming advertisement (general) + * o BLE-only (BR/EDR unsupported). + */ + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; + + /* Indicate that the TX power level field should be included; have the + * stack fill this value automatically. This is done by assiging the + * special value BLE_HS_ADV_TX_PWR_LVL_AUTO. + */ + fields.tx_pwr_lvl_is_present = 1; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + + name = ble_svc_gap_device_name(); + fields.name = (uint8_t *)name; + fields.name_len = strlen(name); + fields.name_is_complete = 1; + + fields.uuids16 = (ble_uuid16_t[]){ + BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID) + }; + fields.num_uuids16 = 1; + fields.uuids16_is_complete = 1; + + rc = ble_gap_adv_set_fields(&fields); + if (rc != 0) { + BSNPRPH_LOG(ERROR, "error setting advertisement data; rc=%d\n", rc); + return; + } + + /* Begin advertising. */ + memset(&adv_params, 0, sizeof adv_params); + adv_params.conn_mode = BLE_GAP_CONN_MODE_DIR; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + adv_params.high_duty_cycle = 1; + rc = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, &bsnprph_central_addr, + BLE_HS_FOREVER, &adv_params, + bsnprph_gap_event, NULL); + if (rc != 0) { + BSNPRPH_LOG(ERROR, "error enabling advertisement; rc=%d\n", rc); + return; + } +} + +static void +bsnprph_tx_timer_reset(void) +{ + int rc; + + rc = os_callout_reset(&bsnprph_tx_timer, BSNPRPH_TX_TIMER_RATE); + assert(rc == 0); +} + +/** + * Transmits dummy data at 60 Hz. + */ +static void +bsnprph_tx_timer_exp(struct os_event *ev) +{ + static uint8_t buf[BSNPRPH_PKT_SZ]; + static uint8_t val; + + struct os_mbuf *om; + int rc; + + memset(buf, val, sizeof buf); + val++; + + om = ble_hs_mbuf_from_flat(buf, sizeof buf); + if (om == NULL) { + /* XXX: OOM; log this. */ + } else { + rc = ble_gattc_notify_custom(bsnprph_conn_handle, + gatt_svr_chr_gendata_val_handle, om); + if (rc != 0) { + /* XXX: Log this. */ + } + } + + bsnprph_tx_timer_reset(); +} + +/** + * The nimble host executes this callback when a GAP event occurs. The + * application associates a GAP event callback with each connection that forms. + * bsnprph uses the same callback for all connections. + * + * @param event The type of event being signalled. + * @param ctxt Various information pertaining to the event. + * @param arg Application-specified argument; unuesd by + * bsnprph. + * + * @return 0 if the application successfully handled the + * event; nonzero on failure. The semantics + * of the return code is specific to the + * particular GAP event being signalled. + */ +static int +bsnprph_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + BSNPRPH_LOG(INFO, "connection %s; status=%d ", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + if (event->connect.status == 0) { + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + bsnprph_print_conn_desc(&desc); + + bsnprph_conn_handle = event->connect.conn_handle; + } + BSNPRPH_LOG(INFO, "\n"); + + if (event->connect.status != 0) { + /* Connection failed; resume advertising. */ + bsnprph_advertise(); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + os_callout_stop(&bsnprph_tx_timer); + + BSNPRPH_LOG(INFO, "disconnect; reason=%d ", event->disconnect.reason); + bsnprph_print_conn_desc(&event->disconnect.conn); + BSNPRPH_LOG(INFO, "\n"); + + /* Connection terminated; resume advertising. */ + bsnprph_advertise(); + return 0; + + case BLE_GAP_EVENT_ADV_COMPLETE: + os_callout_stop(&bsnprph_tx_timer); + BSNPRPH_LOG(INFO, "adv complete\n"); + bsnprph_advertise(); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + /* The central has updated the connection parameters. */ + BSNPRPH_LOG(INFO, "connection updated; status=%d ", + event->conn_update.status); + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + bsnprph_print_conn_desc(&desc); + BSNPRPH_LOG(INFO, "\n"); + return 0; + + case BLE_GAP_EVENT_ENC_CHANGE: + /* Encryption has been enabled or disabled for this connection. */ + BSNPRPH_LOG(INFO, "encryption change event; status=%d ", + event->enc_change.status); + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + bsnprph_print_conn_desc(&desc); + BSNPRPH_LOG(INFO, "\n"); + return 0; + + case BLE_GAP_EVENT_SUBSCRIBE: + BSNPRPH_LOG(INFO, "subscribe event; conn_handle=%d attr_handle=%d " + "reason=%d prevn=%d curn=%d previ=%d curi=%d\n", + event->subscribe.conn_handle, + event->subscribe.attr_handle, + event->subscribe.reason, + event->subscribe.prev_notify, + event->subscribe.cur_notify, + event->subscribe.prev_indicate, + event->subscribe.cur_indicate); + if (event->subscribe.attr_handle == gatt_svr_chr_gendata_val_handle) { + /* Start transmitting notifications. */ + bsnprph_tx_timer_reset(); + } + return 0; + + case BLE_GAP_EVENT_MTU: + BSNPRPH_LOG(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.channel_id, + event->mtu.value); + return 0; + } + + return 0; +} + +static void +bsnprph_on_reset(int reason) +{ + BSNPRPH_LOG(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +bsnprph_on_sync(void) +{ + /* Begin advertising. */ + bsnprph_advertise(); +} + +/** + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +main(void) +{ + int rc; + + /* Initialize OS */ + sysinit(); + + /* Set initial BLE device address. */ + memcpy(g_dev_addr, bsnprph_prph_public_addr, 6); + + /* Initialize the bsnprph log. */ + log_register("bsnprph", &bsnprph_log, &log_console_handler, NULL, + LOG_SYSLEVEL); + + /* Initialize the NimBLE host configuration. */ + log_register("ble_hs", &ble_hs_log, &log_console_handler, NULL, + LOG_SYSLEVEL); + ble_hs_cfg.reset_cb = bsnprph_on_reset; + ble_hs_cfg.sync_cb = bsnprph_on_sync; + ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb; + + os_callout_init(&bsnprph_tx_timer, os_eventq_dflt_get(), + bsnprph_tx_timer_exp, NULL); + + rc = gatt_svr_init(); + assert(rc == 0); + + /* Set the default device name. */ + rc = ble_svc_gap_device_name_set(MYNEWT_VAL(BSNPRPH_BLE_NAME)); + assert(rc == 0); + + conf_load(); + + /* If this app is acting as the loader in a split image setup, jump into + * the second stage application instead of starting the OS. + */ +#if MYNEWT_VAL(SPLIT_LOADER) + { + void *entry; + rc = split_app_go(&entry, true); + if (rc == 0) { + hal_system_start(entry); + } + } +#endif + + /* + * As the last thing, process events from default event queue. + */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/src/misc.c ---------------------------------------------------------------------- diff --git a/apps/bsnprph/src/misc.c b/apps/bsnprph/src/misc.c new file mode 100644 index 0000000..72dc813 --- /dev/null +++ b/apps/bsnprph/src/misc.c @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "bsnprph.h" + +/** + * Utility function to log an array of bytes. + */ +void +print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + BSNPRPH_LOG(INFO, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +void +print_addr(const void *addr) +{ + const uint8_t *u8p; + + u8p = addr; + BSNPRPH_LOG(INFO, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/apps/bsnprph/syscfg.yml ---------------------------------------------------------------------- diff --git a/apps/bsnprph/syscfg.yml b/apps/bsnprph/syscfg.yml new file mode 100644 index 0000000..45082c2 --- /dev/null +++ b/apps/bsnprph/syscfg.yml @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + BSNPRPH_BLE_NAME: + description: The BLE name to use. + value: '"bsnprph"' + +syscfg.vals: + # Use INFO log level to reduce code size. DEBUG is too large for nRF51. + LOG_LEVEL: 1 + + # Disable central and observer roles. + BLE_ROLE_BROADCASTER: 1 + BLE_ROLE_CENTRAL: 0 + BLE_ROLE_OBSERVER: 0 + BLE_ROLE_PERIPHERAL: 1 + + # Disable unused eddystone feature. + BLE_EDDYSTONE: 0 http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/net/nimble/controller/include/controller/ble_ll_conn.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_ll_conn.h b/net/nimble/controller/include/controller/ble_ll_conn.h index d0badff..dd45b16 100644 --- a/net/nimble/controller/include/controller/ble_ll_conn.h +++ b/net/nimble/controller/include/controller/ble_ll_conn.h @@ -154,6 +154,10 @@ struct ble_ll_conn_sm uint8_t last_unmapped_chan; uint8_t num_used_chans; +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) + uint8_t period_occ_mask; /* mask: period 0 = 0x01, period 3 = 0x08 */ +#endif + /* RSSI */ int8_t conn_rssi; http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/net/nimble/controller/include/controller/ble_ll_sched.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_ll_sched.h b/net/nimble/controller/include/controller/ble_ll_sched.h index 3a05cfe..2872730 100644 --- a/net/nimble/controller/include/controller/ble_ll_sched.h +++ b/net/nimble/controller/include/controller/ble_ll_sched.h @@ -69,6 +69,40 @@ extern "C" { struct ble_ll_sched_item; typedef int (*sched_cb_func)(struct ble_ll_sched_item *sch); +/* + * Strict connection scheduling (for the master) is different than how + * connections are normally scheduled. With strict connection scheduling we + * introduce the concept of a "period". A period is a collection of slots. Each + * slot is 1.25 msecs in length. The number of slots in a period is determined + * by the syscfg value BLE_LL_CONN_INIT_SLOTS. A collection of periods is called + * an epoch. The length of an epoch is determined by the number of connections + * (BLE_MAX_CONNECTIONS plus BLE_LL_ADD_STRICT_SCHED_PERIODS). Connections + * will be scheduled at period boundaries. Any scanning/initiating/advertising + * will be done in unused periods, if possible. + */ +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) +#define BLE_LL_SCHED_PERIODS (MYNEWT_VAL(BLE_MAX_CONNECTIONS) + \ + MYNEWT_VAL(BLE_LL_ADD_STRICT_SCHED_PERIODS)) + +struct ble_ll_sched_obj +{ + uint8_t sch_num_occ_periods; + uint32_t sch_occ_period_mask; + uint32_t sch_ticks_per_period; + uint32_t sch_ticks_per_epoch; + uint32_t sch_epoch_start; +}; + +extern struct ble_ll_sched_obj g_ble_ll_sched_data; + +/* + * XXX: TODO: + * -> How do we know epoch start is up to date? Not wrapped? + * -> for now, only do this with no more than 32 connections. + * -> Do not let initiating occur if no empty sched slots + */ +#endif + struct ble_ll_sched_item { uint8_t sched_type; http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/net/nimble/controller/src/ble_ll.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll.c b/net/nimble/controller/src/ble_ll.c index 6071d3c..5db6ba2 100644 --- a/net/nimble/controller/src/ble_ll.c +++ b/net/nimble/controller/src/ble_ll.c @@ -1164,6 +1164,9 @@ ble_ll_reset(void) /* All this does is re-initialize the event masks so call the hci init */ ble_ll_hci_init(); + /* Reset scheduler */ + ble_ll_sched_init(); + /* Set state to standby */ ble_ll_state_set(BLE_LL_STATE_STANDBY); http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/631cc3d9/net/nimble/controller/src/ble_ll_conn.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_conn.c b/net/nimble/controller/src/ble_ll_conn.c index d8d3f8f..7a61d74 100644 --- a/net/nimble/controller/src/ble_ll_conn.c +++ b/net/nimble/controller/src/ble_ll_conn.c @@ -698,6 +698,11 @@ ble_ll_conn_continue_rx_encrypt(void *arg) static uint32_t ble_ll_conn_get_next_sched_time(struct ble_ll_conn_sm *connsm) { + +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) + uint32_t ce_end; + ce_end = connsm->ce_end_time; +#else uint32_t itvl; uint32_t ce_end; uint32_t next_sched_time; @@ -711,6 +716,7 @@ ble_ll_conn_get_next_sched_time(struct ble_ll_conn_sm *connsm) ce_end = next_sched_time; } } +#endif return ce_end; } @@ -1552,6 +1558,9 @@ ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err) uint8_t *evbuf; struct os_mbuf *m; struct os_mbuf_pkthdr *pkthdr; +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) + os_sr_t sr; +#endif /* Remove scheduler events just in case */ ble_ll_sched_rmv_elem(&connsm->conn_sch); @@ -1588,6 +1597,16 @@ ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err) /* Make sure events off queue */ os_eventq_remove(&g_ble_ll_data.ll_evq, &connsm->conn_ev_end); +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) + /* Remove from occupied periods */ + OS_ENTER_CRITICAL(sr); + assert(g_ble_ll_sched_data.sch_num_occ_periods > 0); + assert(g_ble_ll_sched_data.sch_occ_period_mask & connsm->period_occ_mask); + --g_ble_ll_sched_data.sch_num_occ_periods; + g_ble_ll_sched_data.sch_occ_period_mask &= ~connsm->period_occ_mask; + OS_EXIT_CRITICAL(sr); +#endif + /* Connection state machine is now idle */ connsm->conn_state = BLE_LL_CONN_STATE_IDLE; @@ -1611,7 +1630,8 @@ ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err) STAILQ_INSERT_TAIL(&g_ble_ll_conn_free_list, connsm, free_stqe); /* Log connection end */ - ble_ll_log(BLE_LL_LOG_ID_CONN_END,connsm->conn_handle,0,connsm->event_cntr); + ble_ll_log(BLE_LL_LOG_ID_CONN_END,connsm->conn_handle, ble_err, + connsm->event_cntr); } /** @@ -1736,7 +1756,11 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm) * Calculate ce end time. For a slave, we need to add window widening and * the transmit window if we still have one. */ +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) + itvl = g_ble_ll_sched_data.sch_ticks_per_period; +#else itvl = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_USECS_PER_SLOT; +#endif if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { cur_ww = ble_ll_conn_calc_window_widening(connsm); max_ww = (connsm->conn_itvl * (BLE_LL_CONN_ITVL_USECS/2)) - BLE_LL_IFS; @@ -1804,8 +1828,13 @@ ble_ll_conn_created(struct ble_ll_conn_sm *connsm, uint32_t endtime, connsm->tx_win_size * BLE_LL_CONN_TX_WIN_USECS; usecs = 1250 + (connsm->tx_win_off * BLE_LL_CONN_TX_WIN_USECS); connsm->anchor_point = endtime + os_cputime_usecs_to_ticks(usecs); +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) + usecs = connsm->slave_cur_tx_win_usecs + + g_ble_ll_sched_data.sch_ticks_per_period; +#else usecs = connsm->slave_cur_tx_win_usecs + (MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_USECS_PER_SLOT); +#endif connsm->ce_end_time = connsm->anchor_point + os_cputime_usecs_to_ticks(usecs); connsm->slave_cur_window_widening = 0;
