mynewt-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sterl...@apache.org
Subject [5/6] incubator-mynewt-larva git commit: rename project to app
Date Sat, 05 Mar 2016 02:39:19 GMT
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd032817/apps/bletiny/src/cmd.c
----------------------------------------------------------------------
diff --git a/apps/bletiny/src/cmd.c b/apps/bletiny/src/cmd.c
new file mode 100644
index 0000000..ce66a8e
--- /dev/null
+++ b/apps/bletiny/src/cmd.c
@@ -0,0 +1,1479 @@
+/**
+ * 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 <errno.h>
+#include <string.h>
+#include "bsp/bsp.h"
+#include "console/console.h"
+#include "shell/shell.h"
+
+#include "nimble/ble.h"
+#include "nimble/hci_common.h"
+#include "host/ble_gap.h"
+#include "host/ble_hs_adv.h"
+#include "../src/ble_l2cap_priv.h"
+
+#include "bletiny_priv.h"
+
+#define CMD_BUF_SZ      256
+
+static int cmd_b_exec(int argc, char **argv);
+static struct shell_cmd cmd_b = {
+    .sc_cmd = "b",
+    .sc_cmd_func = cmd_b_exec
+};
+
+static bssnz_t uint8_t cmd_buf[CMD_BUF_SZ];
+
+/*****************************************************************************
+ * $misc                                                                     *
+ *****************************************************************************/
+
+static int
+cmd_exec(const struct cmd_entry *cmds, int argc, char **argv)
+{
+    const struct cmd_entry *cmd;
+    int rc;
+
+    if (argc <= 1) {
+        return parse_err_too_few_args(argv[0]);
+    }
+
+    cmd = parse_cmd_find(cmds, argv[1]);
+    if (cmd == NULL) {
+        BLETINY_LOG(ERROR, "Error: unknown %s command: %s\n",
+                    argv[0], argv[1]);
+        return -1;
+    }
+
+    rc = cmd->cb(argc - 1, argv + 1);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+static void
+cmd_print_dsc(struct bletiny_dsc *dsc)
+{
+    BLETINY_LOG(INFO, "            dsc_handle=%d uuid=", dsc->dsc.handle);
+    print_uuid(dsc->dsc.uuid128);
+    BLETINY_LOG(INFO, "\n");
+}
+
+static void
+cmd_print_chr(struct bletiny_chr *chr)
+{
+    struct bletiny_dsc *dsc;
+
+    BLETINY_LOG(INFO, "        def_handle=%d val_handle=%d properties=0x%02x "
+                      "uuid=", chr->chr.decl_handle, chr->chr.value_handle,
+                chr->chr.properties);
+    print_uuid(chr->chr.uuid128);
+    BLETINY_LOG(INFO, "\n");
+
+    SLIST_FOREACH(dsc, &chr->dscs, next) {
+        cmd_print_dsc(dsc);
+    }
+}
+
+static void
+cmd_print_svc(struct bletiny_svc *svc, int print_chrs)
+{
+    struct bletiny_chr *chr;
+
+    BLETINY_LOG(INFO, "    start=%d end=%d uuid=", svc->svc.start_handle,
+                svc->svc.end_handle);
+    print_uuid(svc->svc.uuid128);
+    BLETINY_LOG(INFO, "\n");
+
+    if (print_chrs) {
+        SLIST_FOREACH(chr, &svc->chrs, next) {
+            cmd_print_chr(chr);
+        }
+    }
+}
+
+static 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;
+}
+
+/*****************************************************************************
+ * $advertise                                                                *
+ *****************************************************************************/
+
+static 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 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 struct kv_pair cmd_adv_addr_types[] = {
+    { "public", BLE_ADDR_TYPE_PUBLIC },
+    { "random", BLE_ADDR_TYPE_RANDOM },
+    { NULL }
+};
+
+static 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 },
+};
+
+static int
+cmd_adv(int argc, char **argv)
+{
+    struct hci_adv_params params = {
+        .adv_itvl_min = 0,
+        .adv_itvl_max = 0,
+        .adv_type = BLE_HCI_ADV_TYPE_ADV_IND,
+        .own_addr_type = BLE_HCI_ADV_OWN_ADDR_PUBLIC,
+        .peer_addr_type = BLE_HCI_ADV_PEER_ADDR_PUBLIC,
+        .adv_channel_map = BLE_HCI_ADV_CHANMASK_DEF,
+        .adv_filter_policy = BLE_HCI_ADV_FILT_DEF,
+    };
+    uint8_t peer_addr[6];
+    uint8_t u8;
+    int addr_type;
+    int conn;
+    int disc;
+    int rc;
+
+    if (argc > 1 && strcmp(argv[1], "stop") == 0) {
+        rc = bletiny_adv_stop();
+        if (rc != 0) {
+            BLETINY_LOG(INFO, "advertise stop fail: %d\n", rc);
+            return rc;
+        }
+
+        return 0;
+    }
+
+    conn = parse_arg_kv("conn", cmd_adv_conn_modes);
+    if (conn == -1) {
+        BLETINY_LOG(ERROR, "invalid 'conn' parameter\n");
+        return -1;
+    }
+
+    disc = parse_arg_kv("disc", cmd_adv_disc_modes);
+    if (disc == -1) {
+        BLETINY_LOG(ERROR, "missing 'disc' parameter\n");
+        return -1;
+    }
+
+    if (conn == BLE_GAP_CONN_MODE_DIR) {
+        addr_type = parse_arg_kv("addr_type", cmd_adv_addr_types);
+        if (addr_type == -1) {
+            return -1;
+        }
+
+        rc = parse_arg_mac("addr", peer_addr);
+        if (rc != 0) {
+            return rc;
+        }
+    } else {
+        addr_type = 0;
+        memset(peer_addr, 0, sizeof peer_addr);
+    }
+
+    u8 = parse_arg_long_bounds("chan_map", 0, 0xff, &rc);
+    if (rc == 0) {
+        params.adv_channel_map = u8;
+    } else if (rc != ENOENT) {
+        return rc;
+    }
+
+    if (parse_arg_find("filt") != NULL) {
+        params.adv_filter_policy = parse_arg_kv("filt", cmd_adv_filt_types);
+        if (params.adv_filter_policy == -1) {
+            return EINVAL;
+        }
+    }
+
+    rc = bletiny_adv_start(disc, conn, peer_addr, addr_type, &params);
+    if (rc != 0) {
+        BLETINY_LOG(INFO, "advertise fail: %d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $connect                                                                  *
+ *****************************************************************************/
+
+static struct kv_pair cmd_conn_addr_types[] = {
+    { "public",         BLE_HCI_CONN_PEER_ADDR_PUBLIC },
+    { "random",         BLE_HCI_CONN_PEER_ADDR_RANDOM },
+    { "public_ident",   BLE_HCI_CONN_PEER_ADDR_PUBLIC_IDENT },
+    { "random_ident",   BLE_HCI_CONN_PEER_ADDR_RANDOM_IDENT },
+    { "wl",             BLE_GAP_ADDR_TYPE_WL },
+    { NULL }
+};
+
+static int
+cmd_conn(int argc, char **argv)
+{
+    struct ble_gap_crt_params params;
+    uint8_t peer_addr[6];
+    int addr_type;
+    int rc;
+
+    if (argc > 1 && strcmp(argv[1], "cancel") == 0) {
+        rc = bletiny_conn_cancel();
+        if (rc != 0) {
+            BLETINY_LOG(INFO, "connection cancel fail: %d\n", rc);
+            return rc;
+        }
+
+        return 0;
+    }
+
+    addr_type = parse_arg_kv("addr_type", cmd_conn_addr_types);
+    if (addr_type == -1) {
+        return -1;
+    }
+
+    if (addr_type != BLE_GAP_ADDR_TYPE_WL) {
+        rc = parse_arg_mac("addr", peer_addr);
+        if (rc != 0) {
+            return rc;
+        }
+    } else {
+        memset(peer_addr, 0, sizeof peer_addr);
+    }
+
+    params.scan_itvl = parse_arg_uint16_dflt("scan_itvl", 0x0010, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.scan_window = parse_arg_uint16_dflt("scan_window", 0x0010, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.itvl_min = parse_arg_uint16_dflt(
+        "itvl_min", BLE_GAP_INITIAL_CONN_ITVL_MIN, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.itvl_max = parse_arg_uint16_dflt(
+        "itvl_max", BLE_GAP_INITIAL_CONN_ITVL_MAX, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.latency = parse_arg_uint16_dflt("latency", 0, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.supervision_timeout = parse_arg_uint16_dflt("timeout", 0x0100, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.min_ce_len = parse_arg_uint16_dflt("min_ce_len", 0x0010, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.max_ce_len = parse_arg_uint16_dflt("max_ce_len", 0x0300, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bletiny_conn_initiate(addr_type, peer_addr, &params);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $connect                                                                  *
+ *****************************************************************************/
+
+static int
+cmd_chrup(int argc, char **argv)
+{
+    uint16_t attr_handle;
+    int rc;
+
+    attr_handle = parse_arg_long("attr", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    bletiny_chrup(attr_handle);
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $discover                                                                 *
+ *****************************************************************************/
+
+static int
+cmd_disc_chr(int argc, char **argv)
+{
+    uint16_t start_handle;
+    uint16_t conn_handle;
+    uint16_t end_handle;
+    uint8_t uuid128[16];
+    int rc;
+
+    rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = parse_arg_uuid("uuid", uuid128);
+    if (rc == 0) {
+        rc = bletiny_disc_chrs_by_uuid(conn_handle, start_handle, end_handle,
+                                        uuid128);
+    } else if (rc == ENOENT) {
+        rc = bletiny_disc_all_chrs(conn_handle, start_handle, end_handle);
+    } else  {
+        return rc;
+    }
+    if (rc != 0) {
+        BLETINY_LOG(INFO, "error discovering characteristics; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+static int
+cmd_disc_dsc(int argc, char **argv)
+{
+    uint16_t start_handle;
+    uint16_t conn_handle;
+    uint16_t end_handle;
+    int rc;
+
+    rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bletiny_disc_all_dscs(conn_handle, start_handle, end_handle);
+    if (rc != 0) {
+        BLETINY_LOG(INFO, "error discovering descriptors; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+static int
+cmd_disc_svc(int argc, char **argv)
+{
+    uint8_t uuid128[16];
+    int conn_handle;
+    int rc;
+
+    conn_handle = parse_arg_uint16("conn", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = parse_arg_uuid("uuid", uuid128);
+    if (rc == 0) {
+        rc = bletiny_disc_svc_by_uuid(conn_handle, uuid128);
+    } else if (rc == ENOENT) {
+        rc = bletiny_disc_svcs(conn_handle);
+    } else  {
+        return rc;
+    }
+
+    if (rc != 0) {
+        BLETINY_LOG(INFO, "error discovering services; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+static struct cmd_entry cmd_disc_entries[] = {
+    { "chr", cmd_disc_chr },
+    { "dsc", cmd_disc_dsc },
+    { "svc", cmd_disc_svc },
+    { NULL, NULL }
+};
+
+static int
+cmd_disc(int argc, char **argv)
+{
+    int rc;
+
+    rc = cmd_exec(cmd_disc_entries, argc, argv);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $find                                                                     *
+ *****************************************************************************/
+
+static int
+cmd_find_inc_svcs(int argc, char **argv)
+{
+    uint16_t start_handle;
+    uint16_t conn_handle;
+    uint16_t end_handle;
+    int rc;
+
+    rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bletiny_find_inc_svcs(conn_handle, start_handle, end_handle);
+    if (rc != 0) {
+        BLETINY_LOG(INFO, "error finding included services; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+static const struct cmd_entry cmd_find_entries[] = {
+    { "inc_svcs", cmd_find_inc_svcs },
+    { NULL, NULL }
+};
+
+static int
+cmd_find(int argc, char **argv)
+{
+    int rc;
+
+    rc = cmd_exec(cmd_find_entries, argc, argv);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $l2cap                                                                    *
+ *****************************************************************************/
+
+static int
+cmd_l2cap_update(int argc, char **argv)
+{
+    struct ble_l2cap_sig_update_params params;
+    uint16_t conn_handle;
+    int rc;
+
+    conn_handle = parse_arg_uint16("conn", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.itvl_min = parse_arg_uint16_dflt(
+        "itvl_min", BLE_GAP_INITIAL_CONN_ITVL_MIN, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.itvl_max = parse_arg_uint16_dflt(
+        "itvl_max", BLE_GAP_INITIAL_CONN_ITVL_MAX, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.slave_latency = parse_arg_uint16_dflt("latency", 0, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.timeout_multiplier = parse_arg_uint16_dflt("timeout", 0x0100, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bletiny_l2cap_update(conn_handle, &params);
+    if (rc != 0) {
+        BLETINY_LOG(INFO, "error txing l2cap update; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+static const struct cmd_entry cmd_l2cap_entries[] = {
+    { "update", cmd_l2cap_update },
+    { NULL, NULL }
+};
+
+static int
+cmd_l2cap(int argc, char **argv)
+{
+    int rc;
+
+    rc = cmd_exec(cmd_l2cap_entries, argc, argv);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $mtu                                                                      *
+ *****************************************************************************/
+
+static int
+cmd_mtu(int argc, char **argv)
+{
+    uint16_t conn_handle;
+    int rc;
+
+    conn_handle = parse_arg_uint16("conn", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bletiny_exchange_mtu(conn_handle);
+    if (rc != 0) {
+        BLETINY_LOG(INFO, "error exchanging mtu; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $read                                                                     *
+ *****************************************************************************/
+
+#define CMD_READ_MAX_ATTRS  8
+
+static int
+cmd_read(int argc, char **argv)
+{
+    static uint16_t attr_handles[CMD_READ_MAX_ATTRS];
+    uint16_t conn_handle;
+    uint16_t start;
+    uint16_t end;
+    uint8_t uuid128[16];
+    uint8_t num_attr_handles;
+    int is_uuid;
+    int is_long;
+    int rc;
+
+    conn_handle = parse_arg_uint16("conn", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    is_long = parse_arg_long("long", &rc);
+    if (rc == ENOENT) {
+        is_long = 0;
+    } else if (rc != 0) {
+        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) {
+            return rc;
+        }
+    }
+
+    rc = parse_arg_uuid("uuid", uuid128);
+    if (rc == ENOENT) {
+        is_uuid = 0;
+    } else if (rc == 0) {
+        is_uuid = 1;
+    } else {
+        return rc;
+    }
+
+    start = parse_arg_uint16("start", &rc);
+    if (rc == ENOENT) {
+        start = 0;
+    } else if (rc != 0) {
+        return rc;
+    }
+
+    end = parse_arg_uint16("end", &rc);
+    if (rc == ENOENT) {
+        end = 0;
+    } else if (rc != 0) {
+        return rc;
+    }
+
+    if (num_attr_handles == 1) {
+        if (is_long) {
+            rc = bletiny_read_long(conn_handle, attr_handles[0]);
+        } else {
+            rc = bletiny_read(conn_handle, attr_handles[0]);
+        }
+    } else if (num_attr_handles > 1) {
+        rc = bletiny_read_mult(conn_handle, attr_handles, num_attr_handles);
+    } else if (is_uuid) {
+        if (start == 0 || end == 0) {
+            rc = EINVAL;
+        } else {
+            rc = bletiny_read_by_uuid(conn_handle, start, end, uuid128);
+        }
+    } else {
+        rc = EINVAL;
+    }
+
+    if (rc != 0) {
+        BLETINY_LOG(INFO, "error reading characteristic; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $scan                                                                     *
+ *****************************************************************************/
+
+static struct kv_pair cmd_scan_disc_modes[] = {
+    { "ltd", BLE_GAP_DISC_MODE_LTD },
+    { "gen", BLE_GAP_DISC_MODE_GEN },
+    { NULL }
+};
+
+static struct kv_pair cmd_scan_types[] = {
+    { "passive", BLE_HCI_SCAN_TYPE_PASSIVE },
+    { "active", BLE_HCI_SCAN_TYPE_ACTIVE },
+    { NULL }
+};
+
+static 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 int
+cmd_scan(int argc, char **argv)
+{
+    uint32_t dur;
+    int disc;
+    int type;
+    int filt;
+    int rc;
+
+    dur = parse_arg_uint16("dur", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    disc = parse_arg_kv("disc", cmd_scan_disc_modes);
+    if (disc == -1) {
+        return EINVAL;
+    }
+
+    type = parse_arg_kv("type", cmd_scan_types);
+    if (type == -1) {
+        return EINVAL;
+    }
+
+    filt = parse_arg_kv("filt", cmd_scan_filt_policies);
+    if (disc == -1) {
+        return EINVAL;
+    }
+
+    rc = bletiny_scan(dur, disc, type, filt);
+    if (rc != 0) {
+        BLETINY_LOG(INFO, "error scanning; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $show                                                                     *
+ *****************************************************************************/
+
+static int
+cmd_show_addr(int argc, char **argv)
+{
+    bletiny_lock();
+
+    BLETINY_LOG(INFO, "myaddr=");
+    print_addr(g_dev_addr);
+    BLETINY_LOG(INFO, "\n");
+
+    bletiny_unlock();
+
+    return 0;
+}
+
+static int
+cmd_show_chr(int argc, char **argv)
+{
+    struct bletiny_conn *conn;
+    struct bletiny_svc *svc;
+    int i;
+
+    bletiny_lock();
+
+    for (i = 0; i < bletiny_num_conns; i++) {
+        conn = bletiny_conns + i;
+
+        BLETINY_LOG(INFO, "CONNECTION: handle=%d addr=", conn->handle);
+        print_addr(conn->addr);
+        BLETINY_LOG(INFO, "\n");
+
+        SLIST_FOREACH(svc, &conn->svcs, next) {
+            cmd_print_svc(svc, 1);
+        }
+    }
+
+    bletiny_unlock();
+
+    return 0;
+}
+
+static int
+cmd_show_conn(int argc, char **argv)
+{
+    struct bletiny_conn *conn;
+    int i;
+
+    bletiny_lock();
+
+    for (i = 0; i < bletiny_num_conns; i++) {
+        conn = bletiny_conns + i;
+
+        BLETINY_LOG(INFO, "handle=%d addr=", conn->handle);
+        print_addr(conn->addr);
+        BLETINY_LOG(INFO, " addr_type=%d\n", conn->addr_type);
+    }
+
+    bletiny_unlock();
+
+    return 0;
+}
+
+static int
+cmd_show_rssi(int argc, char **argv)
+{
+    uint16_t conn_handle;
+    int rc;
+
+    conn_handle = parse_arg_uint16("conn", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bletiny_show_rssi(conn_handle);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+static int
+cmd_show_svc(int argc, char **argv)
+{
+    struct bletiny_conn *conn;
+    struct bletiny_svc *svc;
+    int i;
+
+    bletiny_lock();
+
+    for (i = 0; i < bletiny_num_conns; i++) {
+        conn = bletiny_conns + i;
+
+        BLETINY_LOG(INFO, "CONNECTION: handle=%d addr=", conn->handle);
+        print_addr(conn->addr);
+        BLETINY_LOG(INFO, "\n");
+
+        SLIST_FOREACH(svc, &conn->svcs, next) {
+            cmd_print_svc(svc, 0);
+        }
+    }
+
+    bletiny_unlock();
+
+    return 0;
+}
+
+static struct cmd_entry cmd_show_entries[] = {
+    { "addr", cmd_show_addr },
+    { "chr", cmd_show_chr },
+    { "conn", cmd_show_conn },
+    { "rssi", cmd_show_rssi },
+    { "svc", cmd_show_svc },
+    { NULL, NULL }
+};
+
+static int
+cmd_show(int argc, char **argv)
+{
+    int rc;
+
+    rc = cmd_exec(cmd_show_entries, argc, argv);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $set                                                                      *
+ *****************************************************************************/
+
+#define CMD_ADV_DATA_MAX_UUIDS16                8
+#define CMD_ADV_DATA_MAX_UUIDS32                8
+#define CMD_ADV_DATA_MAX_UUIDS128               8
+#define CMD_ADV_DATA_MAX_PUBLIC_TGT_ADDRS       8
+#define CMD_ADV_DATA_SVC_DATA_UUID16_MAX_LEN    32
+#define CMD_ADV_DATA_SVC_DATA_UUID32_MAX_LEN    32
+#define CMD_ADV_DATA_SVC_DATA_UUID128_MAX_LEN   32
+#define CMD_ADV_DATA_URI_MAX_LEN                32
+#define CMD_ADV_DATA_MFG_DATA_MAX_LEN           32
+
+static int
+cmd_set_adv_data(void)
+{
+    static bssnz_t uint16_t uuids16[CMD_ADV_DATA_MAX_UUIDS16];
+    static bssnz_t uint32_t uuids32[CMD_ADV_DATA_MAX_UUIDS32];
+    static bssnz_t uint8_t uuids128[CMD_ADV_DATA_MAX_UUIDS128][16];
+    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 device_class[BLE_HS_ADV_DEVICE_CLASS_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 le_addr[BLE_HS_ADV_LE_ADDR_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];
+    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;
+
+    memset(&adv_fields, 0, sizeof adv_fields);
+
+    while (1) {
+        uuid16 = parse_arg_uint16("uuid16", &rc);
+        if (rc == 0) {
+            if (adv_fields.num_uuids16 >= CMD_ADV_DATA_MAX_UUIDS16) {
+                return EINVAL;
+            }
+            uuids16[adv_fields.num_uuids16] = uuid16;
+            adv_fields.num_uuids16++;
+        } else if (rc == ENOENT) {
+            break;
+        } else {
+            return rc;
+        }
+    }
+    if (adv_fields.num_uuids16 > 0) {
+        adv_fields.uuids16 = uuids16;
+    }
+
+    tmp = parse_arg_long("uuids16_is_complete", &rc);
+    if (rc == 0) {
+        adv_fields.uuids16_is_complete = !!tmp;
+    } else if (rc != ENOENT) {
+        return rc;
+    }
+
+    while (1) {
+        uuid32 = parse_arg_uint32("uuid32", &rc);
+        if (rc == 0) {
+            if (adv_fields.num_uuids32 >= CMD_ADV_DATA_MAX_UUIDS32) {
+                return EINVAL;
+            }
+            uuids32[adv_fields.num_uuids32] = uuid32;
+            adv_fields.num_uuids32++;
+        } else if (rc == ENOENT) {
+            break;
+        } else {
+            return rc;
+        }
+    }
+    if (adv_fields.num_uuids32 > 0) {
+        adv_fields.uuids32 = uuids32;
+    }
+
+    tmp = parse_arg_long("uuids32_is_complete", &rc);
+    if (rc == 0) {
+        adv_fields.uuids32_is_complete = !!tmp;
+    } else if (rc != ENOENT) {
+        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) {
+                return EINVAL;
+            }
+            memcpy(uuids128[adv_fields.num_uuids128], uuid128, 16);
+            adv_fields.num_uuids128++;
+        } else if (rc == ENOENT) {
+            break;
+        } else {
+            return rc;
+        }
+    }
+    if (adv_fields.num_uuids128 > 0) {
+        adv_fields.uuids128 = uuids128;
+    }
+
+    tmp = parse_arg_long("uuids128_is_complete", &rc);
+    if (rc == 0) {
+        adv_fields.uuids128_is_complete = !!tmp;
+    } else if (rc != ENOENT) {
+        return rc;
+    }
+
+    adv_fields.name = (uint8_t *)parse_arg_find("name");
+    if (adv_fields.name != NULL) {
+        adv_fields.name_len = strlen((char *)adv_fields.name);
+    }
+
+    tmp = parse_arg_long_bounds("tx_pwr_lvl", 0, 0xff, &rc);
+    if (rc == 0) {
+        adv_fields.tx_pwr_lvl = tmp;
+        adv_fields.tx_pwr_lvl_is_present = 1;
+    } else if (rc != ENOENT) {
+        return rc;
+    }
+
+    rc = parse_arg_byte_stream_exact_length("device_class", device_class,
+                                            BLE_HS_ADV_DEVICE_CLASS_LEN);
+    if (rc == 0) {
+        adv_fields.device_class = device_class;
+    } else if (rc != ENOENT) {
+        return rc;
+    }
+
+    rc = parse_arg_byte_stream_exact_length("slave_itvl_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) {
+        return rc;
+    }
+
+    rc = parse_arg_byte_stream("svc_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) {
+        return rc;
+    }
+
+    while (1) {
+        rc = parse_arg_byte_stream_exact_length(
+            "public_tgt_addr", 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) {
+
+                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 {
+            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) {
+        return rc;
+    }
+
+    adv_fields.adv_itvl = parse_arg_uint16("adv_itvl", &rc);
+    if (rc == 0) {
+        adv_fields.adv_itvl_is_present = 1;
+    } else if (rc != ENOENT) {
+        return rc;
+    }
+
+    rc = parse_arg_byte_stream_exact_length("le_addr", le_addr,
+                                            BLE_HS_ADV_LE_ADDR_LEN);
+    if (rc == 0) {
+        adv_fields.le_addr = le_addr;
+    } else if (rc != ENOENT) {
+        return rc;
+    }
+
+    adv_fields.le_role = parse_arg_long_bounds("le_role", 0, 0xff, &rc);
+    if (rc == 0) {
+        adv_fields.le_role_is_present = 1;
+    } else if (rc != ENOENT) {
+        return rc;
+    }
+
+    rc = parse_arg_byte_stream("svc_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) {
+        return rc;
+    }
+
+    rc = parse_arg_byte_stream("svc_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) {
+        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) {
+        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) {
+        return rc;
+    }
+
+    rc = bletiny_set_adv_data(&adv_fields);
+    if (rc != 0) {
+        BLETINY_LOG(INFO, "error setting advertisement data; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+static int
+cmd_set(int argc, char **argv)
+{
+    uint16_t mtu;
+    uint8_t addr[6];
+    int good;
+    int rc;
+
+    if (argc > 1 && strcmp(argv[1], "adv_data") == 0) {
+        rc = cmd_set_adv_data();
+        return rc;
+    }
+
+    good = 0;
+
+    rc = parse_arg_mac("addr", addr);
+    if (rc == 0) {
+        good = 1;
+        memcpy(g_dev_addr, addr, 6);
+    } else if (rc != ENOENT) {
+        return rc;
+    }
+
+    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) {
+        return rc;
+    }
+
+    if (!good) {
+        BLETINY_LOG(ERROR, "Error: no valid settings specified\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $terminate                                                                *
+ *****************************************************************************/
+
+static int
+cmd_term(int argc, char **argv)
+{
+    uint16_t conn_handle;
+    int rc;
+
+    conn_handle = parse_arg_uint16("conn", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bletiny_term_conn(conn_handle);
+    if (rc != 0) {
+        BLETINY_LOG(INFO, "error terminating connection; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $update connection parameters                                             *
+ *****************************************************************************/
+
+static int
+cmd_update(int argc, char **argv)
+{
+    struct ble_gap_upd_params params;
+    uint16_t conn_handle;
+    int rc;
+
+    conn_handle = parse_arg_uint16("conn", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.itvl_min = parse_arg_uint16_dflt(
+        "itvl_min", BLE_GAP_INITIAL_CONN_ITVL_MIN, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.itvl_max = parse_arg_uint16_dflt(
+        "itvl_max", BLE_GAP_INITIAL_CONN_ITVL_MAX, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.latency = parse_arg_uint16_dflt("latency", 0, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.supervision_timeout = parse_arg_uint16_dflt("timeout", 0x0100, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.min_ce_len = parse_arg_uint16_dflt("min_ce_len", 0x0010, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    params.max_ce_len = parse_arg_uint16_dflt("max_ce_len", 0x0300, &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bletiny_update_conn(conn_handle, &params);
+    if (rc != 0) {
+        BLETINY_LOG(INFO, "error updating connection; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $white list                                                               *
+ *****************************************************************************/
+
+static struct kv_pair cmd_wl_addr_types[] = {
+    { "public",         BLE_HCI_CONN_PEER_ADDR_PUBLIC },
+    { "random",         BLE_HCI_CONN_PEER_ADDR_RANDOM },
+    { NULL }
+};
+
+#define CMD_WL_MAX_SZ   8
+
+static int
+cmd_wl(int argc, char **argv)
+{
+    static struct ble_gap_white_entry white_list[CMD_WL_MAX_SZ];
+    uint8_t addr_type;
+    uint8_t addr[6];
+    int wl_cnt;
+    int rc;
+
+    wl_cnt = 0;
+    while (1) {
+        if (wl_cnt >= CMD_WL_MAX_SZ) {
+            return EINVAL;
+        }
+
+        rc = parse_arg_mac("addr", addr);
+        if (rc == ENOENT) {
+            break;
+        } else if (rc != 0) {
+            return rc;
+        }
+
+        addr_type = parse_arg_kv("addr_type", cmd_wl_addr_types);
+        if (addr_type == -1) {
+            return EINVAL;
+        }
+
+        memcpy(white_list[wl_cnt].addr, addr, 6);
+        white_list[wl_cnt].addr_type = addr_type;
+        wl_cnt++;
+    }
+
+    if (wl_cnt == 0) {
+        return EINVAL;
+    }
+
+    bletiny_wl_set(white_list, wl_cnt);
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $write                                                                    *
+ *****************************************************************************/
+
+#define CMD_WRITE_MAX_ATTRS 16
+
+static int
+cmd_write(int argc, char **argv)
+{
+    static struct ble_gatt_attr attrs[CMD_WRITE_MAX_ATTRS];
+    uint16_t attr_handle;
+    uint16_t conn_handle;
+    int total_attr_len;
+    int num_attrs;
+    int attr_len;
+    int is_long;
+    int no_rsp;
+    int rc;
+
+    conn_handle = parse_arg_uint16("conn", &rc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    no_rsp = parse_arg_long("no_rsp", &rc);
+    if (rc == ENOENT) {
+        no_rsp = 0;
+    } else if (rc != 0) {
+        return rc;
+    }
+
+    is_long = parse_arg_long("long", &rc);
+    if (rc == ENOENT) {
+        is_long = 0;
+    } else if (rc != 0) {
+        return rc;
+    }
+
+    total_attr_len = 0;
+    num_attrs = 0;
+    while (1) {
+        attr_handle = parse_arg_long("attr", &rc);
+        if (rc == ENOENT) {
+            break;
+        } else if (rc != 0) {
+            return rc;
+        }
+
+        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) {
+            return rc;
+        }
+
+        if (num_attrs >= CMD_WRITE_MAX_ATTRS) {
+            return EINVAL;
+        }
+
+        attrs[num_attrs].handle = attr_handle;
+        attrs[num_attrs].offset = 0;
+        attrs[num_attrs].value_len = attr_len;
+        attrs[num_attrs].value = cmd_buf + total_attr_len;
+
+        total_attr_len += attr_len;
+        num_attrs++;
+    }
+
+    if (no_rsp) {
+        if (num_attrs != 1) {
+            return EINVAL;
+        }
+        rc = bletiny_write_no_rsp(conn_handle, attrs[0].handle,
+                                   attrs[0].value, attrs[0].value_len);
+    } else if (is_long) {
+        if (num_attrs != 1) {
+            return EINVAL;
+        }
+        rc = bletiny_write_long(conn_handle, attrs[0].handle,
+                                 attrs[0].value, attrs[0].value_len);
+    } else if (num_attrs > 1) {
+        rc = bletiny_write_reliable(conn_handle, attrs, num_attrs);
+    } else if (num_attrs == 1) {
+        rc = bletiny_write(conn_handle, attrs[0].handle,
+                            attrs[0].value, attrs[0].value_len);
+    } else {
+        return EINVAL;
+    }
+
+    if (rc != 0) {
+        BLETINY_LOG(INFO, "error writing characteristic; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $init                                                                     *
+ *****************************************************************************/
+
+static struct cmd_entry cmd_b_entries[] = {
+    { "adv",        cmd_adv },
+    { "conn",       cmd_conn },
+    { "chrup",      cmd_chrup },
+    { "disc",       cmd_disc },
+    { "find",       cmd_find },
+    { "l2cap",      cmd_l2cap },
+    { "mtu",        cmd_mtu },
+    { "read",       cmd_read },
+    { "scan",       cmd_scan },
+    { "show",       cmd_show },
+    { "set",        cmd_set },
+    { "term",       cmd_term },
+    { "update",     cmd_update },
+    { "wl",         cmd_wl },
+    { "write",      cmd_write },
+    { NULL, NULL }
+};
+
+static int
+cmd_b_exec(int argc, char **argv)
+{
+    int rc;
+
+    rc = parse_arg_all(argc - 1, argv + 1);
+    if (rc != 0) {
+        goto done;
+    }
+
+    rc = cmd_exec(cmd_b_entries, argc, argv);
+    if (rc != 0) {
+        BLETINY_LOG(ERROR, "error\n");
+        goto done;
+    }
+
+    rc = 0;
+
+done:
+    return rc;
+}
+
+int
+cmd_init(void)
+{
+    int rc;
+
+    rc = shell_cmd_register(&cmd_b);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd032817/apps/bletiny/src/main.c
----------------------------------------------------------------------
diff --git a/apps/bletiny/src/main.c b/apps/bletiny/src/main.c
new file mode 100755
index 0000000..cf482ad
--- /dev/null
+++ b/apps/bletiny/src/main.c
@@ -0,0 +1,1467 @@
+/**
+ * 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 "bsp/bsp.h"
+#include "log/log.h"
+#include "stats/stats.h"
+#include "os/os.h"
+#include "bsp/bsp.h"
+#include "hal/hal_gpio.h"
+#include "hal/hal_cputime.h"
+#include "console/console.h"
+#include "shell/shell.h"
+#include "bletiny_priv.h"
+
+/* BLE */
+#include "nimble/ble.h"
+#include "nimble/nimble_opt.h"
+#include "host/host_hci.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 "controller/ble_ll.h"
+#include "../src/ble_hs_conn.h"
+#include "../src/ble_hci_sched.h"
+
+#define BSWAP16(x)  ((uint16_t)(((x) << 8) | (((x) & 0xff00) >> 8)))
+
+/* Task 1 */
+#define HOST_TASK_PRIO          (1)
+
+#define SHELL_TASK_PRIO         (3)
+#define SHELL_MAX_INPUT_LEN     (64)
+#define SHELL_TASK_STACK_SIZE   (OS_STACK_ALIGN(216))
+static bssnz_t os_stack_t shell_stack[SHELL_TASK_STACK_SIZE];
+
+static struct os_mutex bletiny_mutex;
+
+/* Our global device address (public) */
+uint8_t g_dev_addr[BLE_DEV_ADDR_LEN];
+
+/* Our random address (in case we need it) */
+uint8_t g_random_addr[BLE_DEV_ADDR_LEN];
+
+/* A buffer for host advertising data */
+uint8_t g_host_adv_data[BLE_HCI_MAX_ADV_DATA_LEN];
+uint8_t g_host_adv_len;
+
+static uint8_t bletiny_addr[6] = {0x03, 0x02, 0x01, 0x50, 0x13, 0x00};
+
+/* Create a mbuf pool of BLE mbufs */
+#define MBUF_NUM_MBUFS      (7)
+#define MBUF_BUF_SIZE       OS_ALIGN(BLE_MBUF_PAYLOAD_SIZE, 4)
+#define MBUF_MEMBLOCK_SIZE  (MBUF_BUF_SIZE + BLE_MBUF_MEMBLOCK_OVERHEAD)
+#define MBUF_MEMPOOL_SIZE   OS_MEMPOOL_SIZE(MBUF_NUM_MBUFS, MBUF_MEMBLOCK_SIZE)
+
+os_membuf_t default_mbuf_mpool_data[MBUF_MEMPOOL_SIZE];
+
+struct os_mbuf_pool default_mbuf_pool;
+struct os_mempool default_mbuf_mpool;
+
+/* BLETINY variables */
+#define BLETINY_STACK_SIZE             (OS_STACK_ALIGN(200))
+#define BLETINY_TASK_PRIO              (HOST_TASK_PRIO + 1)
+
+#if NIMBLE_OPT_ROLE_CENTRAL
+#define BLETINY_MAX_SVCS               32
+#define BLETINY_MAX_CHRS               64
+#define BLETINY_MAX_DSCS               64
+#else
+#define BLETINY_MAX_SVCS               1
+#define BLETINY_MAX_CHRS               1
+#define BLETINY_MAX_DSCS               1
+#endif
+
+struct os_eventq g_bletiny_evq;
+struct os_task bletiny_task;
+bssnz_t os_stack_t bletiny_stack[BLETINY_STACK_SIZE];
+
+static struct log_handler bletiny_log_console_handler;
+struct log bletiny_log;
+
+struct bletiny_conn ble_shell_conns[NIMBLE_OPT_MAX_CONNECTIONS];
+int bletiny_num_conns;
+
+void
+bletest_inc_adv_pkt_num(void) { }
+
+bssnz_t struct bletiny_conn bletiny_conns[NIMBLE_OPT_MAX_CONNECTIONS];
+int bletiny_num_conns;
+
+static void *bletiny_svc_mem;
+static struct os_mempool bletiny_svc_pool;
+
+static void *bletiny_chr_mem;
+static struct os_mempool bletiny_chr_pool;
+
+static void *bletiny_dsc_mem;
+static struct os_mempool bletiny_dsc_pool;
+
+const char *bletiny_device_name = "nimble-bletiny";
+const uint16_t bletiny_appearance = BSWAP16(BLE_GAP_APPEARANCE_GEN_COMPUTER);
+const uint8_t bletiny_privacy_flag = 0;
+uint8_t bletiny_reconnect_addr[6];
+uint8_t bletiny_pref_conn_params[8];
+uint8_t bletiny_gatt_service_changed[4];
+
+#define XSTR(s) STR(s)
+#define STR(s) #s
+
+#ifdef DEVICE_NAME
+#define BLETINY_AUTO_DEVICE_NAME    XSTR(DEVICE_NAME)
+#else
+#define BLETINY_AUTO_DEVICE_NAME    NULL
+#endif
+
+void
+bletiny_lock(void)
+{
+    int rc;
+
+    rc = os_mutex_pend(&bletiny_mutex, 0xffffffff);
+    assert(rc == 0 || rc == OS_NOT_STARTED);
+}
+
+void
+bletiny_unlock(void)
+{
+    int rc;
+
+    rc = os_mutex_release(&bletiny_mutex);
+    assert(rc == 0 || rc == OS_NOT_STARTED);
+}
+
+static void
+bletiny_print_error(char *msg, uint16_t conn_handle,
+                    struct ble_gatt_error *error)
+{
+    BLETINY_LOG(INFO, "%s: conn_handle=%d status=%d att_handle=%d\n",
+                msg, conn_handle, error->status, error->att_handle);
+}
+
+static void
+bletiny_print_bytes(uint8_t *bytes, int len)
+{
+    int i;
+
+    for (i = 0; i < len; i++) {
+        BLETINY_LOG(INFO, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
+    }
+}
+
+static void
+bletiny_print_conn_desc(struct ble_gap_conn_desc *desc)
+{
+    BLETINY_LOG(INFO, "handle=%d peer_addr_type=%d peer_addr=",
+                desc->conn_handle, desc->peer_addr_type);
+    bletiny_print_bytes(desc->peer_addr, 6);
+    BLETINY_LOG(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d",
+                desc->conn_itvl, desc->conn_latency,
+                desc->supervision_timeout);
+}
+
+static void
+bletiny_print_adv_fields(struct ble_hs_adv_fields *fields)
+{
+    uint32_t u32;
+    uint16_t u16;
+    uint8_t *u8p;
+    int i;
+
+    if (fields->flags_is_present) {
+        BLETINY_LOG(INFO, "    flags=0x%02x\n", fields->flags);
+    }
+
+    if (fields->uuids16 != NULL) {
+        BLETINY_LOG(INFO, "    uuids16(%scomplete)=",
+                    fields->uuids16_is_complete ? "" : "in");
+        u8p = fields->uuids16;
+        for (i = 0; i < fields->num_uuids16; i++) {
+            memcpy(&u16, u8p + i * 2, 2);
+            BLETINY_LOG(INFO, "0x%04x ", u16);
+        }
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    if (fields->uuids32 != NULL) {
+        BLETINY_LOG(INFO, "    uuids32(%scomplete)=",
+                    fields->uuids32_is_complete ? "" : "in");
+        u8p = fields->uuids32;
+        for (i = 0; i < fields->num_uuids32; i++) {
+            memcpy(&u32, u8p + i * 4, 4);
+            BLETINY_LOG(INFO, "0x%08x ", (unsigned)u32);
+        }
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    if (fields->uuids128 != NULL) {
+        BLETINY_LOG(INFO, "    uuids128(%scomplete)=",
+                    fields->uuids128_is_complete ? "" : "in");
+        u8p = fields->uuids128;
+        for (i = 0; i < fields->num_uuids128; i++) {
+            print_uuid(u8p);
+            BLETINY_LOG(INFO, " ");
+            u8p += 16;
+        }
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    if (fields->name != NULL) {
+        BLETINY_LOG(INFO, "    name(%scomplete)=",
+                    fields->name_is_complete ? "" : "in");
+        BLETINY_LOG(INFO, "%*s\n", fields->name_len, fields->name);
+    }
+
+    if (fields->tx_pwr_lvl_is_present) {
+        BLETINY_LOG(INFO, "    tx_pwr_lvl=%d\n", fields->tx_pwr_lvl);
+    }
+
+    if (fields->device_class != NULL) {
+        BLETINY_LOG(INFO, "    device_class=");
+        bletiny_print_bytes(fields->device_class,
+                             BLE_HS_ADV_DEVICE_CLASS_LEN);
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    if (fields->slave_itvl_range != NULL) {
+        BLETINY_LOG(INFO, "    slave_itvl_range=");
+        bletiny_print_bytes(fields->slave_itvl_range,
+                             BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    if (fields->svc_data_uuid16 != NULL) {
+        BLETINY_LOG(INFO, "    svc_data_uuid16=");
+        bletiny_print_bytes(fields->svc_data_uuid16,
+                             fields->svc_data_uuid16_len);
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    if (fields->public_tgt_addr != NULL) {
+        BLETINY_LOG(INFO, "    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;
+        }
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    if (fields->appearance_is_present) {
+        BLETINY_LOG(INFO, "    appearance=0x%04x\n", fields->appearance);
+    }
+
+    if (fields->adv_itvl_is_present) {
+        BLETINY_LOG(INFO, "    adv_itvl=0x%04x\n", fields->adv_itvl);
+    }
+
+    if (fields->le_addr != NULL) {
+        BLETINY_LOG(INFO, "    le_addr=");
+        bletiny_print_bytes(fields->le_addr, BLE_HS_ADV_LE_ADDR_LEN);
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    if (fields->le_role_is_present) {
+        BLETINY_LOG(INFO, "    le_role=0x%02x\n", fields->le_role);
+    }
+
+    if (fields->svc_data_uuid32 != NULL) {
+        BLETINY_LOG(INFO, "    svc_data_uuid32=");
+        bletiny_print_bytes(fields->svc_data_uuid32,
+                             fields->svc_data_uuid32_len);
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    if (fields->svc_data_uuid128 != NULL) {
+        BLETINY_LOG(INFO, "    svc_data_uuid128=");
+        bletiny_print_bytes(fields->svc_data_uuid128,
+                             fields->svc_data_uuid128_len);
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    if (fields->uri != NULL) {
+        BLETINY_LOG(INFO, "    uri=");
+        bletiny_print_bytes(fields->uri, fields->uri_len);
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    if (fields->mfg_data != NULL) {
+        BLETINY_LOG(INFO, "    mfg_data=");
+        bletiny_print_bytes(fields->mfg_data, fields->mfg_data_len);
+        BLETINY_LOG(INFO, "\n");
+    }
+}
+
+static int
+bletiny_conn_find_idx(uint16_t handle)
+{
+    int i;
+
+    for (i = 0; i < bletiny_num_conns; i++) {
+        if (bletiny_conns[i].handle == handle) {
+            return i;
+        }
+    }
+
+    return -1;
+}
+
+static struct bletiny_conn *
+bletiny_conn_find(uint16_t handle)
+{
+    int idx;
+
+    idx = bletiny_conn_find_idx(handle);
+    if (idx == -1) {
+        return NULL;
+    } else {
+        return bletiny_conns + idx;
+    }
+}
+
+static struct bletiny_conn *
+bletiny_conn_add(struct ble_gap_conn_desc *desc)
+{
+    struct bletiny_conn *conn;
+
+    assert(bletiny_num_conns < NIMBLE_OPT_MAX_CONNECTIONS);
+
+    conn = bletiny_conns + bletiny_num_conns;
+    bletiny_num_conns++;
+
+    conn->handle = desc->conn_handle;
+    conn->addr_type = desc->peer_addr_type;
+    memcpy(conn->addr, desc->peer_addr, 6);
+    SLIST_INIT(&conn->svcs);
+
+    return conn;
+}
+
+static void
+bletiny_conn_delete_idx(int idx)
+{
+    struct bletiny_conn *conn;
+    struct bletiny_svc *svc;
+
+    assert(idx >= 0 && idx < bletiny_num_conns);
+
+    conn = bletiny_conns + idx;
+    while ((svc = SLIST_FIRST(&conn->svcs)) != NULL) {
+        SLIST_REMOVE_HEAD(&conn->svcs, next);
+        os_memblock_put(&bletiny_svc_pool, svc);
+    }
+
+    bletiny_num_conns--;
+
+    /* This '#if' is not strictly necessary.  It is here to prevent a spurious
+     * warning from being reported.
+     */
+#if NIMBLE_OPT_MAX_CONNECTIONS > 1
+    int i;
+    for (i = idx; i < bletiny_num_conns; i++) {
+        bletiny_conns[i - 1] = bletiny_conns[i];
+    }
+#endif
+}
+
+static struct bletiny_svc *
+bletiny_svc_find_prev(struct bletiny_conn *conn, uint16_t svc_start_handle)
+{
+    struct bletiny_svc *prev;
+    struct bletiny_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 bletiny_svc *
+bletiny_svc_find(struct bletiny_conn *conn, uint16_t svc_start_handle,
+                  struct bletiny_svc **out_prev)
+{
+    struct bletiny_svc *prev;
+    struct bletiny_svc *svc;
+
+    prev = bletiny_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 bletiny_svc *
+bletiny_svc_find_range(struct bletiny_conn *conn, uint16_t attr_handle)
+{
+    struct bletiny_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 struct bletiny_svc *
+bletiny_svc_add(uint16_t conn_handle, struct ble_gatt_service *gatt_svc)
+{
+    struct bletiny_conn *conn;
+    struct bletiny_svc *prev;
+    struct bletiny_svc *svc;
+
+    conn = bletiny_conn_find(conn_handle);
+    if (conn == NULL) {
+        BLETINY_LOG(INFO,
+                    "RECEIVED SERVICE FOR UNKNOWN CONNECTION; HANDLE=%d\n",
+                    conn_handle);
+        return NULL;
+    }
+
+    svc = bletiny_svc_find(conn, gatt_svc->start_handle, &prev);
+    if (svc != NULL) {
+        /* Service already discovered. */
+        return svc;
+    }
+
+    svc = os_memblock_get(&bletiny_svc_pool);
+    if (svc == NULL) {
+        BLETINY_LOG(INFO, "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 bletiny_chr *
+bletiny_chr_find_prev(struct bletiny_svc *svc, uint16_t chr_def_handle)
+{
+    struct bletiny_chr *prev;
+    struct bletiny_chr *chr;
+
+    prev = NULL;
+    SLIST_FOREACH(chr, &svc->chrs, next) {
+        if (chr->chr.decl_handle >= chr_def_handle) {
+            break;
+        }
+
+        prev = chr;
+    }
+
+    return prev;
+}
+
+static struct bletiny_chr *
+bletiny_chr_find(struct bletiny_svc *svc, uint16_t chr_def_handle,
+                  struct bletiny_chr **out_prev)
+{
+    struct bletiny_chr *prev;
+    struct bletiny_chr *chr;
+
+    prev = bletiny_chr_find_prev(svc, chr_def_handle);
+    if (prev == NULL) {
+        chr = SLIST_FIRST(&svc->chrs);
+    } else {
+        chr = SLIST_NEXT(prev, next);
+    }
+
+    if (chr != NULL && chr->chr.decl_handle != chr_def_handle) {
+        chr = NULL;
+    }
+
+    if (out_prev != NULL) {
+        *out_prev = prev;
+    }
+    return chr;
+}
+
+
+static struct bletiny_chr *
+bletiny_chr_add(uint16_t conn_handle,  uint16_t svc_start_handle,
+                 struct ble_gatt_chr *gatt_chr)
+{
+    struct bletiny_conn *conn;
+    struct bletiny_chr *prev;
+    struct bletiny_chr *chr;
+    struct bletiny_svc *svc;
+
+    conn = bletiny_conn_find(conn_handle);
+    if (conn == NULL) {
+        BLETINY_LOG(INFO,
+                    "RECEIVED SERVICE FOR UNKNOWN CONNECTION; HANDLE=%d\n",
+                    conn_handle);
+        return NULL;
+    }
+
+    svc = bletiny_svc_find(conn, svc_start_handle, NULL);
+    if (svc == NULL) {
+        BLETINY_LOG(INFO,
+                    "CAN'T FIND SERVICE FOR DISCOVERED CHR; HANDLE=%d\n",
+                    conn_handle);
+        return NULL;
+    }
+
+    chr = bletiny_chr_find(svc, gatt_chr->decl_handle, &prev);
+    if (chr != NULL) {
+        /* Characteristic already discovered. */
+        return chr;
+    }
+
+    chr = os_memblock_get(&bletiny_chr_pool);
+    if (chr == NULL) {
+        BLETINY_LOG(INFO, "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 bletiny_dsc *
+bletiny_dsc_find_prev(struct bletiny_chr *chr, uint16_t dsc_handle)
+{
+    struct bletiny_dsc *prev;
+    struct bletiny_dsc *dsc;
+
+    prev = NULL;
+    SLIST_FOREACH(dsc, &chr->dscs, next) {
+        if (dsc->dsc.handle >= dsc_handle) {
+            break;
+        }
+
+        prev = dsc;
+    }
+
+    return prev;
+}
+
+static struct bletiny_dsc *
+bletiny_dsc_find(struct bletiny_chr *chr, uint16_t dsc_handle,
+                  struct bletiny_dsc **out_prev)
+{
+    struct bletiny_dsc *prev;
+    struct bletiny_dsc *dsc;
+
+    prev = bletiny_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 bletiny_dsc *
+bletiny_dsc_add(uint16_t conn_handle, uint16_t chr_def_handle,
+                 struct ble_gatt_dsc *gatt_dsc)
+{
+    struct bletiny_conn *conn;
+    struct bletiny_dsc *prev;
+    struct bletiny_dsc *dsc;
+    struct bletiny_svc *svc;
+    struct bletiny_chr *chr;
+
+    conn = bletiny_conn_find(conn_handle);
+    if (conn == NULL) {
+        BLETINY_LOG(INFO,
+                    "RECEIVED SERVICE FOR UNKNOWN CONNECTION; HANDLE=%d\n",
+                    conn_handle);
+        return NULL;
+    }
+
+    svc = bletiny_svc_find_range(conn, chr_def_handle);
+    if (svc == NULL) {
+        BLETINY_LOG(INFO,
+                    "CAN'T FIND SERVICE FOR DISCOVERED DSC; HANDLE=%d\n",
+                    conn_handle);
+        return NULL;
+    }
+
+    chr = bletiny_chr_find(svc, chr_def_handle, NULL);
+    if (chr == NULL) {
+        BLETINY_LOG(INFO, "CAN'T FIND CHARACTERISTIC FOR DISCOVERED DSC; "
+                    "HANDLE=%d\n", conn_handle);
+        return NULL;
+    }
+
+    dsc = bletiny_dsc_find(chr, gatt_dsc->handle, &prev);
+    if (dsc != NULL) {
+        /* Descriptor already discovered. */
+        return dsc;
+    }
+
+    dsc = os_memblock_get(&bletiny_dsc_pool);
+    if (dsc == NULL) {
+        BLETINY_LOG(INFO, "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 void
+bletiny_start_auto_advertise(void)
+{
+#if 0
+    struct ble_hs_adv_fields fields;
+    int rc;
+
+    if (BLETINY_AUTO_DEVICE_NAME != NULL) {
+        memset(&fields, 0, sizeof fields);
+
+        fields.name = (uint8_t *)BLETINY_AUTO_DEVICE_NAME;
+        fields.name_len = strlen(BLETINY_AUTO_DEVICE_NAME);
+        fields.name_is_complete = 1;
+        rc = bletiny_set_adv_data(&fields);
+        if (rc != 0) {
+            return;
+        }
+
+        rc = bletiny_adv_start(BLE_GAP_DISC_MODE_GEN, BLE_GAP_CONN_MODE_UND,
+                               NULL, 0, NULL);
+        if (rc != 0) {
+            return;
+        }
+    }
+#endif
+}
+
+static int
+bletiny_on_mtu(uint16_t conn_handle, struct ble_gatt_error *error,
+                uint16_t mtu, void *arg)
+{
+    if (error != NULL) {
+        bletiny_print_error("ERROR EXCHANGING MTU", conn_handle, error);
+    } else {
+        BLETINY_LOG(INFO, "mtu exchange complete: conn_handle=%d mtu=%d\n",
+                    conn_handle, mtu);
+    }
+
+    return 0;
+}
+
+static int
+bletiny_on_disc_s(uint16_t conn_handle, struct ble_gatt_error *error,
+                   struct ble_gatt_service *service, void *arg)
+{
+    bletiny_lock();
+
+    if (error != NULL) {
+        bletiny_print_error("ERROR DISCOVERING SERVICE", conn_handle, error);
+    } else if (service != NULL) {
+        bletiny_svc_add(conn_handle, service);
+    } else {
+        BLETINY_LOG(INFO, "service discovery successful\n");
+    }
+
+    bletiny_unlock();
+
+    return 0;
+}
+
+static int
+bletiny_on_disc_c(uint16_t conn_handle, struct ble_gatt_error *error,
+                   struct ble_gatt_chr *chr, void *arg)
+{
+    intptr_t svc_start_handle;
+
+    svc_start_handle = (intptr_t)arg;
+
+    if (error != NULL) {
+        bletiny_print_error("ERROR DISCOVERING CHARACTERISTIC", conn_handle,
+                             error);
+    } else if (chr != NULL) {
+        bletiny_lock();
+        bletiny_chr_add(conn_handle, svc_start_handle, chr);
+        bletiny_unlock();
+    } else {
+        BLETINY_LOG(INFO, "characteristic discovery successful\n");
+    }
+
+    return 0;
+}
+
+static int
+bletiny_on_disc_d(uint16_t conn_handle, struct ble_gatt_error *error,
+                   uint16_t chr_def_handle, struct ble_gatt_dsc *dsc,
+                   void *arg)
+{
+    bletiny_lock();
+
+    if (error != NULL) {
+        bletiny_print_error("ERROR DISCOVERING DESCRIPTOR", conn_handle,
+                             error);
+    } else if (dsc != NULL) {
+        bletiny_dsc_add(conn_handle, chr_def_handle, dsc);
+    } else {
+        BLETINY_LOG(INFO, "descriptor discovery successful\n");
+    }
+
+    bletiny_unlock();
+
+    return 0;
+}
+
+static int
+bletiny_on_read(uint16_t conn_handle, struct ble_gatt_error *error,
+                 struct ble_gatt_attr *attr, void *arg)
+{
+    if (error != NULL) {
+        bletiny_print_error("ERROR READING CHARACTERISTIC", conn_handle,
+                             error);
+    } else if (attr != NULL) {
+        BLETINY_LOG(INFO, "characteristic read; conn_handle=%d "
+                    "attr_handle=%d len=%d value=", conn_handle,
+                    attr->handle, attr->value_len);
+        bletiny_print_bytes(attr->value, attr->value_len);
+        BLETINY_LOG(INFO, "\n");
+    } else {
+        BLETINY_LOG(INFO, "characteristic read complete\n");
+    }
+
+    return 0;
+}
+
+static int
+bletiny_on_read_mult(uint16_t conn_handle, struct ble_gatt_error *error,
+                      uint16_t *attr_handles, uint8_t num_attr_handles,
+                      uint8_t *attr_data, uint16_t attr_data_len, void *arg)
+{
+    int i;
+
+    if (error != NULL) {
+        bletiny_print_error("ERROR READING CHARACTERISTICS", conn_handle,
+                             error);
+    } else {
+        BLETINY_LOG(INFO,
+                    "multiple characteristic read complete; conn_handle=%d "
+                    "attr_handles=", conn_handle);
+        for (i = 0; i < num_attr_handles; i++) {
+            BLETINY_LOG(INFO, "%s%d", i != 0 ? "," : "", attr_handles[i]);
+        }
+
+        BLETINY_LOG(INFO, " len=%d value=", attr_data_len);
+        bletiny_print_bytes(attr_data, attr_data_len);
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    return 0;
+
+}
+
+static int
+bletiny_on_write(uint16_t conn_handle, struct ble_gatt_error *error,
+                  struct ble_gatt_attr *attr, void *arg)
+{
+    if (error != NULL) {
+        bletiny_print_error("ERROR WRITING CHARACTERISTIC", conn_handle,
+                             error);
+    } else {
+        BLETINY_LOG(INFO, "characteristic write complete; conn_handle=%d "
+                    "attr_handle=%d len=%d value=", conn_handle,
+                    attr->handle, attr->value_len);
+        bletiny_print_bytes(attr->value, attr->value_len);
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    return 0;
+}
+
+static int
+bletiny_on_write_reliable(uint16_t conn_handle, struct ble_gatt_error *error,
+                           struct ble_gatt_attr *attrs, uint8_t num_attrs,
+                           void *arg)
+{
+    int i;
+
+    if (error != NULL) {
+        bletiny_print_error("ERROR WRITING CHARACTERISTICS RELIABLY",
+                             conn_handle, error);
+    } else {
+        BLETINY_LOG(INFO, "characteristic write reliable complete; "
+                    "conn_handle=%d", conn_handle);
+
+        for (i = 0; i < num_attrs; i++) {
+            BLETINY_LOG(INFO, " attr_handle=%d len=%d value=", attrs[i].handle,
+                        attrs[i].value_len);
+            bletiny_print_bytes(attrs[i].value, attrs[i].value_len);
+        }
+        BLETINY_LOG(INFO, "\n");
+    }
+
+    return 0;
+}
+
+static int
+bletiny_on_notify(uint16_t conn_handle, uint16_t attr_handle,
+                   uint8_t *attr_val, uint16_t attr_len, void *arg)
+{
+    BLETINY_LOG(INFO, "received notification from conn_handle=%d attr=%d "
+                "len=%d value=", conn_handle, attr_handle, attr_len);
+
+    bletiny_print_bytes(attr_val, attr_len);
+    BLETINY_LOG(INFO, "\n");
+
+    return 0;
+}
+
+static int
+bletiny_on_connect(int event, int status, struct ble_gap_conn_ctxt *ctxt,
+                    void *arg)
+{
+    int conn_idx;
+
+    bletiny_lock();
+
+    switch (event) {
+    case BLE_GAP_EVENT_CONN:
+        BLETINY_LOG(INFO, "connection %s; status=%d ",
+                    status == 0 ? "up" : "down", status);
+        bletiny_print_conn_desc(ctxt->desc);
+        BLETINY_LOG(INFO, "\n");
+
+        if (status == 0) {
+            bletiny_conn_add(ctxt->desc);
+        } else {
+            if (ctxt->desc->conn_handle == BLE_HS_CONN_HANDLE_NONE) {
+                if (status == BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID)) {
+                    BLETINY_LOG(INFO, "connection procedure cancelled.\n");
+                }
+            } else {
+                conn_idx = bletiny_conn_find_idx(ctxt->desc->conn_handle);
+                if (conn_idx == -1) {
+                    BLETINY_LOG(INFO, "UNKNOWN CONNECTION\n");
+                } else {
+                    bletiny_conn_delete_idx(conn_idx);
+                }
+
+                bletiny_start_auto_advertise();
+            }
+        }
+
+        break;
+
+    case BLE_GAP_EVENT_CONN_UPDATED:
+        BLETINY_LOG(INFO, "connection updated; status=%d ", status);
+        bletiny_print_conn_desc(ctxt->desc);
+        BLETINY_LOG(INFO, "\n");
+        break;
+
+    case BLE_GAP_EVENT_CONN_UPDATE_REQ:
+        BLETINY_LOG(INFO, "connection update request; status=%d ", status);
+        *ctxt->self_params = *ctxt->peer_params;
+        break;
+    }
+
+    bletiny_unlock();
+
+    return 0;
+}
+
+static void
+bletiny_on_l2cap_update(int status, void *arg)
+{
+    BLETINY_LOG(INFO, "l2cap update complete; status=%d\n", status);
+}
+
+static void
+bletiny_on_wl_set(int status, void *arg)
+{
+    BLETINY_LOG(INFO, "white list set status=%d\n", status);
+}
+
+static void
+bletiny_on_scan(int event, int status, struct ble_gap_disc_desc *desc,
+                 void *arg)
+{
+    switch (event) {
+    case BLE_GAP_EVENT_DISC_SUCCESS:
+        BLETINY_LOG(INFO, "received advertisement; event_type=%d addr_type=%d "
+                    "addr=", desc->event_type, desc->addr_type);
+        bletiny_print_bytes(desc->addr, 6);
+        BLETINY_LOG(INFO, " length_data=%d rssi=%d data=", desc->length_data,
+                    desc->rssi);
+        bletiny_print_bytes(desc->data, desc->length_data);
+        BLETINY_LOG(INFO, " fields:\n");
+        bletiny_print_adv_fields(desc->fields);
+        BLETINY_LOG(INFO, "\n");
+        break;
+
+    case BLE_GAP_EVENT_DISC_FINISHED:
+        BLETINY_LOG(INFO, "scanning finished; status=%d\n", status);
+        break;
+
+    default:
+        assert(0);
+        break;
+    }
+}
+
+static void
+bletiny_on_rx_rssi(struct ble_hci_ack *ack, void *unused)
+{
+    struct hci_read_rssi_ack_params params;
+
+    if (ack->bha_params_len != BLE_HCI_READ_RSSI_ACK_PARAM_LEN) {
+        BLETINY_LOG(INFO, "invalid rssi response; len=%d\n",
+                    ack->bha_params_len);
+        return;
+    }
+
+    params.status = ack->bha_params[0];
+    params.connection_handle = le16toh(ack->bha_params + 1);
+    params.rssi = ack->bha_params[3];
+
+    BLETINY_LOG(INFO, "rssi response received; status=%d conn=%d rssi=%d\n",
+                params.status, params.connection_handle, params.rssi);
+}
+
+int
+bletiny_exchange_mtu(uint16_t conn_handle)
+{
+    int rc;
+
+    rc = ble_gattc_exchange_mtu(conn_handle, bletiny_on_mtu, NULL);
+    return rc;
+}
+
+int
+bletiny_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,
+                                 bletiny_on_disc_c, (void *)svc_start_handle);
+    return rc;
+}
+
+int
+bletiny_disc_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle,
+                           uint16_t end_handle, uint8_t *uuid128)
+{
+    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,
+                                     uuid128, bletiny_on_disc_c,
+                                     &svc_start_handle);
+    return rc;
+}
+
+int
+bletiny_disc_svcs(uint16_t conn_handle)
+{
+    int rc;
+
+    rc = ble_gattc_disc_all_svcs(conn_handle, bletiny_on_disc_s, NULL);
+    return rc;
+}
+
+int
+bletiny_disc_svc_by_uuid(uint16_t conn_handle, uint8_t *uuid128)
+{
+    int rc;
+
+    rc = ble_gattc_disc_svc_by_uuid(conn_handle, uuid128,
+                                    bletiny_on_disc_s, NULL);
+    return rc;
+}
+
+int
+bletiny_disc_all_dscs(uint16_t conn_handle, uint16_t chr_def_handle,
+                       uint16_t chr_end_handle)
+{
+    int rc;
+
+    rc = ble_gattc_disc_all_dscs(conn_handle, chr_def_handle, chr_end_handle,
+                                 bletiny_on_disc_d, NULL);
+    return rc;
+}
+
+int
+bletiny_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,
+                                 bletiny_on_disc_s, NULL);
+    return rc;
+}
+
+int
+bletiny_read(uint16_t conn_handle, uint16_t attr_handle)
+{
+    int rc;
+
+    rc = ble_gattc_read(conn_handle, attr_handle, bletiny_on_read, NULL);
+    return rc;
+}
+
+int
+bletiny_read_long(uint16_t conn_handle, uint16_t attr_handle)
+{
+    int rc;
+
+    rc = ble_gattc_read_long(conn_handle, attr_handle, bletiny_on_read, NULL);
+    return rc;
+}
+
+int
+bletiny_read_by_uuid(uint16_t conn_handle, uint16_t start_handle,
+                      uint16_t end_handle, uint8_t *uuid128)
+{
+    int rc;
+
+    rc = ble_gattc_read_by_uuid(conn_handle, start_handle, end_handle, uuid128,
+                                bletiny_on_read, NULL);
+    return rc;
+}
+
+int
+bletiny_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,
+                             bletiny_on_read_mult, NULL);
+    return rc;
+}
+
+int
+bletiny_write(uint16_t conn_handle, uint16_t attr_handle, void *value,
+               uint16_t value_len)
+{
+    int rc;
+
+    if (conn_handle == BLE_HS_CONN_HANDLE_NONE) {
+        rc = ble_att_svr_write_local(attr_handle, value, value_len);
+    } else {
+        rc = ble_gattc_write(conn_handle, attr_handle, value, value_len,
+                             bletiny_on_write, NULL);
+    }
+
+    return rc;
+}
+
+int
+bletiny_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle, void *value,
+                      uint16_t value_len)
+{
+    int rc;
+
+    rc = ble_gattc_write_no_rsp(conn_handle, attr_handle, value, value_len,
+                                bletiny_on_write, NULL);
+    return rc;
+}
+
+int
+bletiny_write_long(uint16_t conn_handle, uint16_t attr_handle, void *value,
+                    uint16_t value_len)
+{
+    int rc;
+
+    rc = ble_gattc_write_long(conn_handle, attr_handle, value, value_len,
+                              bletiny_on_write, NULL);
+    return rc;
+}
+
+int
+bletiny_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,
+                                  bletiny_on_write_reliable, NULL);
+    return rc;
+}
+
+int
+bletiny_adv_stop(void)
+{
+    int rc;
+
+    rc = ble_gap_adv_stop();
+    return rc;
+}
+
+int
+bletiny_adv_start(int disc, int conn, uint8_t *peer_addr, int addr_type,
+                  struct hci_adv_params *params)
+{
+    int rc;
+
+    rc = ble_gap_adv_start(disc, conn, peer_addr, addr_type, params,
+                           bletiny_on_connect, NULL);
+    return rc;
+}
+
+int
+bletiny_conn_initiate(int addr_type, uint8_t *peer_addr,
+                       struct ble_gap_crt_params *params)
+{
+    int rc;
+
+    rc = ble_gap_conn_initiate(addr_type, peer_addr, NULL, bletiny_on_connect,
+                               params);
+    return rc;
+}
+
+int
+bletiny_conn_cancel(void)
+{
+    int rc;
+
+    rc = ble_gap_cancel();
+    return rc;
+}
+
+int
+bletiny_term_conn(uint16_t conn_handle)
+{
+    int rc;
+
+    rc = ble_gap_terminate(conn_handle);
+    return rc;
+}
+
+int
+bletiny_wl_set(struct ble_gap_white_entry *white_list, int white_list_count)
+{
+    int rc;
+
+    rc = ble_gap_wl_set(white_list, white_list_count, bletiny_on_wl_set,
+                        NULL);
+    return rc;
+}
+
+int
+bletiny_scan(uint32_t dur_ms, uint8_t disc_mode, uint8_t scan_type,
+              uint8_t filter_policy)
+{
+    int rc;
+
+    rc = ble_gap_disc(dur_ms, disc_mode, scan_type, filter_policy,
+                      bletiny_on_scan, NULL);
+    return rc;
+}
+
+int
+bletiny_set_adv_data(struct ble_hs_adv_fields *adv_fields)
+{
+    int rc;
+
+    rc = ble_gap_adv_set_fields(adv_fields);
+    return rc;
+}
+
+int
+bletiny_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
+bletiny_chrup(uint16_t attr_handle)
+{
+    ble_gatts_chr_updated(attr_handle);
+}
+
+int
+bletiny_l2cap_update(uint16_t conn_handle,
+                     struct ble_l2cap_sig_update_params *params)
+{
+    int rc;
+
+    rc = ble_l2cap_sig_update(conn_handle, params, bletiny_on_l2cap_update,
+                              NULL);
+    return rc;
+}
+
+static int
+bletiny_tx_rssi_req(void *arg)
+{
+    intptr_t conn_handle;
+    int rc;
+
+    conn_handle = (intptr_t)arg;
+
+    ble_hci_sched_set_ack_cb(bletiny_on_rx_rssi, NULL);
+
+    rc = host_hci_cmd_read_rssi(conn_handle);
+    if (rc != 0) {
+        BLETINY_LOG(ERROR, "failure to send rssi hci cmd; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bletiny_show_rssi(uint16_t conn_handle)
+{
+    int rc;
+
+    rc = ble_hci_sched_enqueue(bletiny_tx_rssi_req,
+                               (void *)(intptr_t)conn_handle, NULL);
+    if (rc != 0) {
+        BLETINY_LOG(ERROR, "failure to enqueue rssi hci cmd; rc=%d\n", rc);
+        return rc;
+    }
+
+    return 0;
+
+}
+
+/**
+ * BLE test task
+ *
+ * @param arg
+ */
+static void
+bletiny_task_handler(void *arg)
+{
+    struct os_event *ev;
+    struct os_callout_func *cf;
+
+    periph_init();
+
+    ble_att_set_notify_cb(bletiny_on_notify, NULL);
+
+    /* Initialize eventq */
+    os_eventq_init(&g_bletiny_evq);
+
+    bletiny_start_auto_advertise();
+
+    while (1) {
+        ev = os_eventq_get(&g_bletiny_evq);
+        switch (ev->ev_type) {
+        case OS_EVENT_T_TIMER:
+            cf = (struct os_callout_func *)ev;
+            assert(cf->cf_func);
+            cf->cf_func(cf->cf_arg);
+            break;
+        default:
+            assert(0);
+            break;
+        }
+    }
+}
+
+/**
+ * main
+ *
+ * The main function for the project. This function initializes the os, calls
+ * init_tasks to initialize tasks (and possibly other objects), then starts the
+ * OS. We should not return from os start.
+ *
+ * @return int NOTE: this function should never return!
+ */
+int
+main(void)
+{
+    struct ble_hs_cfg cfg;
+    uint32_t seed;
+    int rc;
+    int i;
+
+    /* Initialize OS */
+    os_init();
+
+    /* Set cputime to count at 1 usec increments */
+    rc = cputime_init(1000000);
+    assert(rc == 0);
+
+    /* Dummy device address */
+    memcpy(g_dev_addr, bletiny_addr, 6);
+
+    /*
+     * Seed random number generator with least significant bytes of device
+     * address.
+     */
+    seed = 0;
+    for (i = 0; i < 4; ++i) {
+        seed |= g_dev_addr[i];
+        seed <<= 8;
+    }
+    srand(seed);
+
+    bletiny_svc_mem = malloc(
+        OS_MEMPOOL_BYTES(BLETINY_MAX_SVCS, sizeof (struct bletiny_svc)));
+    assert(bletiny_svc_mem != NULL);
+
+    rc = os_mempool_init(&bletiny_svc_pool, BLETINY_MAX_SVCS,
+                         sizeof (struct bletiny_svc), bletiny_svc_mem,
+                         "bletiny_svc_pool");
+    assert(rc == 0);
+
+    bletiny_chr_mem = malloc(
+        OS_MEMPOOL_BYTES(BLETINY_MAX_CHRS, sizeof (struct bletiny_chr)));
+    assert(bletiny_chr_mem != NULL);
+
+    rc = os_mempool_init(&bletiny_chr_pool, BLETINY_MAX_CHRS,
+                         sizeof (struct bletiny_chr), bletiny_chr_mem,
+                         "bletiny_chr_pool");
+    assert(rc == 0);
+
+    bletiny_dsc_mem = malloc(
+        OS_MEMPOOL_BYTES(BLETINY_MAX_DSCS, sizeof (struct bletiny_dsc)));
+    assert(bletiny_dsc_mem != NULL);
+
+    rc = os_mempool_init(&bletiny_dsc_pool, BLETINY_MAX_DSCS,
+                         sizeof (struct bletiny_dsc), bletiny_dsc_mem,
+                         "bletiny_dsc_pool");
+    assert(rc == 0);
+
+    rc = os_mempool_init(&default_mbuf_mpool, MBUF_NUM_MBUFS, 
+                         MBUF_MEMBLOCK_SIZE, default_mbuf_mpool_data, 
+                         "default_mbuf_data");
+    assert(rc == 0);
+
+    rc = os_mbuf_pool_init(&default_mbuf_pool, &default_mbuf_mpool,
+                           MBUF_MEMBLOCK_SIZE, MBUF_NUM_MBUFS);
+    assert(rc == 0);
+
+    rc = os_msys_register(&default_mbuf_pool);
+    assert(rc == 0);
+
+    log_init();
+    log_console_handler_init(&bletiny_log_console_handler);
+    log_register("bletiny", &bletiny_log, &bletiny_log_console_handler);
+
+    os_task_init(&bletiny_task, "bletiny", bletiny_task_handler,
+                 NULL, BLETINY_TASK_PRIO, OS_WAIT_FOREVER,
+                 bletiny_stack, BLETINY_STACK_SIZE);
+
+    rc = shell_task_init(SHELL_TASK_PRIO, shell_stack, SHELL_TASK_STACK_SIZE,
+                         SHELL_MAX_INPUT_LEN);
+    assert(rc == 0);
+
+    /* Init the console */
+    rc = console_init(shell_console_rx_cb);
+    assert(rc == 0);
+
+    rc = stats_module_init();
+    assert(rc == 0);
+
+    /* Initialize the BLE host. */
+    cfg = ble_hs_cfg_dflt;
+    cfg.max_hci_bufs = 3;
+    cfg.max_attrs = 32;
+    cfg.max_services = 4;
+    cfg.max_client_configs = 6;
+    cfg.max_gattc_procs = 2;
+    cfg.max_l2cap_chans = 3;
+    cfg.max_l2cap_sig_procs = 2;
+
+    rc = ble_hs_init(HOST_TASK_PRIO, &cfg);
+    assert(rc == 0);
+
+    /* Initialize the BLE LL */
+    ble_ll_init();
+
+    rc = cmd_init();
+    assert(rc == 0);
+
+    /* Initialize the preferred parameters. */
+    htole16(bletiny_pref_conn_params + 0, BLE_GAP_INITIAL_CONN_ITVL_MIN);
+    htole16(bletiny_pref_conn_params + 2, BLE_GAP_INITIAL_CONN_ITVL_MAX);
+    htole16(bletiny_pref_conn_params + 4, 0);
+    htole16(bletiny_pref_conn_params + 6, BSWAP16(0x100));
+
+    rc = os_mutex_init(&bletiny_mutex);
+    assert(rc == 0);
+
+    /* Start the OS */
+    os_start();
+
+    /* os start should never return. If it does, this should be an error */
+    assert(0);
+
+    return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd032817/apps/bletiny/src/parse.c
----------------------------------------------------------------------
diff --git a/apps/bletiny/src/parse.c b/apps/bletiny/src/parse.c
new file mode 100644
index 0000000..8051a88
--- /dev/null
+++ b/apps/bletiny/src/parse.c
@@ -0,0 +1,435 @@
+/**
+ * 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 "console/console.h"
+#include "host/ble_uuid.h"
+#include "bletiny_priv.h"
+
+#define CMD_MAX_ARGS        16
+
+static char *cmd_args[CMD_MAX_ARGS][2];
+static int cmd_num_args;
+
+void
+print_addr(void *addr)
+{
+    uint8_t *u8p;
+
+    u8p = addr;
+    BLETINY_LOG(INFO, "%02x:%02x:%02x:%02x:%02x:%02x",
+                u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
+}
+
+void
+print_uuid(void *uuid128)
+{
+    uint16_t uuid16;
+    uint8_t *u8p;
+
+    uuid16 = ble_uuid_128_to_16(uuid128);
+    if (uuid16 != 0) {
+        BLETINY_LOG(INFO, "0x%04x", uuid16);
+        return;
+    }
+
+    u8p = uuid128;
+
+    /* 00001101-0000-1000-8000-00805f9b34fb */
+    BLETINY_LOG(INFO, "%02x%02x%02x%02x-", u8p[15], u8p[14], u8p[13],
+                u8p[12]);
+    BLETINY_LOG(INFO, "%02x%02x-%02x%02x-", u8p[11], u8p[10], u8p[9], u8p[8]);
+    BLETINY_LOG(INFO, "%02x%02x%02x%02x%02x%02x%02x%02x",
+                u8p[7], u8p[6], u8p[5], u8p[4],
+                u8p[3], u8p[2], u8p[1], u8p[0]);
+}
+
+int
+parse_err_too_few_args(char *cmd_name)
+{
+    BLETINY_LOG(ERROR, "Error: too few arguments for command \"%s\"\n",
+                cmd_name);
+    return -1;
+}
+
+const struct cmd_entry *
+parse_cmd_find(const struct cmd_entry *cmds, char *name)
+{
+    const struct cmd_entry *cmd;
+    int i;
+
+    for (i = 0; cmds[i].name != NULL; i++) {
+        cmd = cmds + i;
+        if (strcmp(name, cmd->name) == 0) {
+            return cmd;
+        }
+    }
+
+    return NULL;
+}
+
+struct kv_pair *
+parse_kv_find(struct kv_pair *kvs, char *name)
+{
+    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;
+}
+
+char *
+parse_arg_find(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;
+}
+
+long
+parse_arg_long_bounds(char *name, long min, long max, int *out_status)
+{
+    char *endptr;
+    char *sval;
+    long lval;
+
+    sval = parse_arg_find(name);
+    if (sval == NULL) {
+        *out_status = ENOENT;
+        return 0;
+    }
+
+    lval = strtol(sval, &endptr, 0);
+    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);
+}
+
+uint16_t
+parse_arg_uint16(char *name, int *out_status)
+{
+    return parse_arg_long_bounds(name, 0, UINT16_MAX, out_status);
+}
+
+uint32_t
+parse_arg_uint32(char *name, int *out_status)
+{
+    return parse_arg_long_bounds(name, 0, UINT32_MAX, out_status);
+}
+
+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;
+}
+
+int
+parse_arg_kv(char *name, struct kv_pair *kvs)
+{
+    struct kv_pair *kv;
+    char *sval;
+
+    sval = parse_arg_find(name);
+    if (sval == NULL) {
+        return ENOENT;
+    }
+
+    kv = parse_kv_find(kvs, sval);
+    if (kv == NULL) {
+        return EINVAL;
+    }
+
+    return kv->val;
+}
+
+static int
+parse_arg_byte_stream_no_delim(char *sval, int max_len, uint8_t *dst,
+                               int *out_len)
+{
+    unsigned long ul;
+    char *endptr;
+    char buf[3];
+    int i;
+
+    buf[2] = '\0';
+    i = 0;
+    while (1) {
+        if (sval[i * 2] == '\0') {
+            *out_len = i;
+            return 0;
+        }
+
+        if (i >= max_len) {
+            return EINVAL;
+        }
+
+        buf[0] = sval[i * 2 + 0];
+        buf[1] = sval[i * 2 + 1];
+
+        ul = strtoul(buf, &endptr, 16);
+        if (*sval == '\0' || *endptr != '\0') {
+            return EINVAL;
+        }
+
+        assert(ul <= UINT8_MAX);
+        dst[i] = ul;
+
+        i++;
+    }
+}
+
+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)
+{
+    int total_len;
+    char *sval;
+
+    sval = parse_arg_find(name);
+    if (sval == NULL) {
+        return ENOENT;
+    }
+
+    total_len = strlen(sval);
+    if (strcspn(sval, ":-") == total_len) {
+        return parse_arg_byte_stream_no_delim(sval, max_len, dst, out_len);
+    } else {
+        return parse_arg_byte_stream_delim(sval, ":-", 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, 6, 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_uuid(char *str, uint8_t *dst_uuid128)
+{
+    uint16_t uuid16;
+    char *tok;
+    int rc;
+
+    uuid16 = parse_arg_uint16(str, &rc);
+    switch (rc) {
+    case ENOENT:
+        return ENOENT;
+
+    case 0:
+        rc = ble_uuid_16_to_128(uuid16, dst_uuid128);
+        if (rc != 0) {
+            return EINVAL;
+        } else {
+            return 0;
+        }
+
+    default:
+        /* e7add801-b042-4876-aae1112855353cc1 */
+        if (strlen(str) == 35) {
+            tok = strtok(str, "-");
+            if (tok == NULL) {
+                return EINVAL;
+            }
+            rc = parse_arg_byte_stream_exact_length(tok, dst_uuid128 + 0, 4);
+            if (rc != 0) {
+                return rc;
+            }
+
+            tok = strtok(NULL, "-");
+            if (tok == NULL) {
+                return EINVAL;
+            }
+            rc = parse_arg_byte_stream_exact_length(tok, dst_uuid128 + 4, 2);
+            if (rc != 0) {
+                return rc;
+            }
+
+            tok = strtok(NULL, "-");
+            if (tok == NULL) {
+                return EINVAL;
+            }
+            rc = parse_arg_byte_stream_exact_length(tok, dst_uuid128 + 6, 2);
+            if (rc != 0) {
+                return rc;
+            }
+
+            tok = strtok(NULL, "-");
+            if (tok == NULL) {
+                return EINVAL;
+            }
+            rc = parse_arg_byte_stream_exact_length(tok, dst_uuid128 + 8, 8);
+            if (rc != 0) {
+                return rc;
+            }
+
+            return 0;
+        }
+
+        rc = parse_arg_byte_stream_exact_length(str, dst_uuid128, 16);
+        return rc;
+    }
+}
+
+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) {
+                BLETINY_LOG(ERROR, "Error: invalid argument: %s\n", argv[i]);
+                return -1;
+            }
+
+            if (cmd_num_args >= CMD_MAX_ARGS) {
+                BLETINY_LOG(ERROR, "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;
+}
+



Mime
View raw message