| /** |
| * 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 "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 "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_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 = "runtime-1"; |
| 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); |
| |
| /* 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 = 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 = 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; |
| } |