This adds gatt-discover-characteristic which works as follow: btshell>gatt-discover-characteristic <bdaddr> <bdaddr_type> [UUID] [start_handle] [end_handle] bt: bt_gatt_discover_characteristic (0x0010d098): start_handle 0x000b end_handle 0x0011 Discover pending btshell>bt: bt_att_recv (0x0010f294): Received ATT code 0x09 len 23 bt: att_handle_read_type_rsp (0x0010f294): bt: att_read_type_rsp (0x0010f294): err 0x00 bt: att_read_type_rsp (0x0010f294): handle 0x000b properties 0x02 value_handle 0x000c Discover found handle 11 bt: att_read_type_rsp (0x0010f294): handle 0x000d properties 0x10 value_handle 0x000e Discover found handle 13 bt: att_read_type_rsp (0x0010f294): handle 0x0010 properties 0x08 value_handle 0x0011 Discover found handle 16 Discover destroy Change-Id: I04aad56cc4f978e3a884adffc487ae4dcc1c4ee9 Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
492 lines
10 KiB
C
492 lines
10 KiB
C
/** @file
|
|
* @brief Interactive Bluetooth LE shell application
|
|
*
|
|
* Application allows implement Bluetooth LE functional commands performing
|
|
* simple diagnostic interaction between LE host stack and LE controller
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2015 Intel Corporation
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1) Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2) Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3) Neither the name of Intel Corporation nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <misc/printk.h>
|
|
|
|
#include <console/uart_console.h>
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/gatt.h>
|
|
|
|
#include "btshell.h"
|
|
|
|
static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t evtype,
|
|
const uint8_t *ad, uint8_t len)
|
|
{
|
|
char le_addr[BT_ADDR_LE_STR_LEN];
|
|
|
|
bt_addr_le_to_str(addr, le_addr, sizeof(le_addr));
|
|
printk("[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n",
|
|
le_addr, evtype, len, rssi);
|
|
}
|
|
|
|
static void connected(struct bt_conn *conn)
|
|
{
|
|
char addr[BT_ADDR_LE_STR_LEN];
|
|
|
|
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
|
|
|
printk("Connected: %s\n", addr);
|
|
}
|
|
|
|
static void disconnected(struct bt_conn *conn)
|
|
{
|
|
char addr[BT_ADDR_LE_STR_LEN];
|
|
|
|
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
|
|
|
printk("Disconnected: %s\n", addr);
|
|
}
|
|
|
|
static struct bt_conn_cb conn_callbacks = {
|
|
.connected = connected,
|
|
.disconnected = disconnected,
|
|
};
|
|
|
|
static void cmd_init(int argc, char *argv[])
|
|
{
|
|
int err;
|
|
|
|
err = bt_init();
|
|
if (err) {
|
|
printk("Bluetooth init failed (err %d)\n", err);
|
|
} else {
|
|
printk("Bluetooth initialized\n");
|
|
}
|
|
}
|
|
|
|
static int xtoi(const char *str)
|
|
{
|
|
int val = 0;
|
|
|
|
for (; *str != '\0'; str++) {
|
|
val = val << 4;
|
|
if (*str >= '0' && *str <= '9') {
|
|
val |= *str - '0';
|
|
} else if (*str >= 'a' && *str <= 'f') {
|
|
val |= *str - 'a' + 10;
|
|
} else if (*str >= 'A' && *str <= 'F') {
|
|
val |= *str - 'A' + 10;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static int str2bt_addr_le(const char *str, const char *type, bt_addr_le_t *addr)
|
|
{
|
|
int i, j;
|
|
|
|
if (strlen(str) != 17) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 5, j = 1; *str != '\0'; str++, j++) {
|
|
if (!(j % 3) && (*str != ':')) {
|
|
return -EINVAL;
|
|
} else if (*str == ':') {
|
|
i--;
|
|
continue;
|
|
}
|
|
|
|
addr->val[i] = addr->val[i] << 4;
|
|
|
|
if (*str >= '0' && *str <= '9') {
|
|
addr->val[i] |= *str - '0';
|
|
} else if (*str >= 'a' && *str <= 'f') {
|
|
addr->val[i] |= *str - 'a' + 10;
|
|
} else if (*str >= 'A' && *str <= 'F') {
|
|
addr->val[i] |= *str - 'A' + 10;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (!strcmp(type, "public") || !strcmp(type, "(public)")) {
|
|
addr->type = BT_ADDR_LE_PUBLIC;
|
|
} else if (!strcmp(type, "random") || !strcmp(type, "(random)")) {
|
|
addr->type = BT_ADDR_LE_RANDOM;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void cmd_connect_le(int argc, char *argv[])
|
|
{
|
|
int err;
|
|
bt_addr_le_t addr;
|
|
struct bt_conn *conn;
|
|
|
|
if (argc < 2) {
|
|
printk("Peer address required\n");
|
|
return;
|
|
}
|
|
|
|
if (argc < 3) {
|
|
printk("Peer address type required\n");
|
|
return;
|
|
}
|
|
|
|
err = str2bt_addr_le(argv[1], argv[2], &addr);
|
|
if (err) {
|
|
printk("Invalid peer address (err %d)\n", err);
|
|
return;
|
|
}
|
|
|
|
conn = bt_connect_le(&addr);
|
|
|
|
if (!conn) {
|
|
printk("Connection failed\n");
|
|
} else {
|
|
|
|
printk("Connection pending\n");
|
|
|
|
/* unref connection obj in advance as app user */
|
|
bt_conn_put(conn);
|
|
}
|
|
}
|
|
|
|
static void cmd_disconnect(int argc, char *argv[])
|
|
{
|
|
int err;
|
|
bt_addr_le_t addr;
|
|
struct bt_conn *conn;
|
|
|
|
if (argc < 2) {
|
|
printk("Peer address required\n");
|
|
return;
|
|
}
|
|
|
|
if (argc < 3) {
|
|
printk("Peer address type required\n");
|
|
return;
|
|
}
|
|
|
|
err = str2bt_addr_le(argv[1], argv[2], &addr);
|
|
if (err) {
|
|
printk("Invalid peer address (err %d)\n", err);
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_addr_le(&addr);
|
|
if (!conn) {
|
|
printk("Peer not connected\n");
|
|
return;
|
|
}
|
|
|
|
err = bt_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
|
|
if (err) {
|
|
printk("Disconnection failed (err %d)\n", err);
|
|
}
|
|
|
|
bt_conn_put(conn);
|
|
}
|
|
|
|
static void cmd_active_scan_on(void)
|
|
{
|
|
int err;
|
|
|
|
err = bt_start_scanning(BT_LE_SCAN_FILTER_DUP_ENABLE, device_found);
|
|
if (err) {
|
|
printk("Bluetooth set active scan failed (err %d)\n", err);
|
|
} else {
|
|
printk("Bluetooth active scan enabled\n");
|
|
}
|
|
}
|
|
|
|
static void cmd_scan_off(void)
|
|
{
|
|
int err;
|
|
|
|
err = bt_stop_scanning();
|
|
if (err) {
|
|
printk("Stopping scanning failed (err %d)\n", err);
|
|
} else {
|
|
printk("Scan successfully stopped\n");
|
|
}
|
|
}
|
|
|
|
static void cmd_scan(int argc, char *argv[])
|
|
{
|
|
const char *action;
|
|
|
|
if (argc < 2) {
|
|
printk("Scan [on/off] parameter required\n");
|
|
return;
|
|
}
|
|
|
|
action = (char*)argv[1];
|
|
if (!strncmp(action, "on", strlen("on"))) {
|
|
cmd_active_scan_on();
|
|
} else if (!strncmp(action, "off", strlen("off"))) {
|
|
cmd_scan_off();
|
|
} else {
|
|
printk("Scan [on/off] parameter required\n");
|
|
}
|
|
}
|
|
|
|
static void cmd_security(int argc, char *argv[])
|
|
{
|
|
int err, sec;
|
|
bt_addr_le_t addr;
|
|
struct bt_conn *conn;
|
|
|
|
if (argc < 2) {
|
|
printk("Peer address required\n");
|
|
return;
|
|
}
|
|
|
|
if (argc < 3) {
|
|
printk("Peer address type required\n");
|
|
return;
|
|
}
|
|
|
|
if (argc < 4) {
|
|
printk("Security level required\n");
|
|
return;
|
|
}
|
|
|
|
err = str2bt_addr_le(argv[1], argv[2], &addr);
|
|
if (err) {
|
|
printk("Invalid peer address (err %d)\n", err);
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_addr_le(&addr);
|
|
if (!conn) {
|
|
printk("Peer not connected\n");
|
|
return;
|
|
}
|
|
|
|
sec = *argv[3] - '0';
|
|
|
|
err = bt_security(conn, sec);
|
|
if (err) {
|
|
printk("Setting security failed (err %d)\n", err);
|
|
}
|
|
|
|
bt_conn_put(conn);
|
|
}
|
|
|
|
static void exchange_rsp(struct bt_conn *conn, uint8_t err)
|
|
{
|
|
printk("Exchange %s\n", err == 0 ? "successful" : "failed");
|
|
}
|
|
|
|
static void cmd_gatt_exchange_mtu(int argc, char *argv[])
|
|
{
|
|
int err;
|
|
bt_addr_le_t addr;
|
|
struct bt_conn *conn;
|
|
|
|
if (argc < 2) {
|
|
printk("Peer address required\n");
|
|
return;
|
|
}
|
|
|
|
if (argc < 3) {
|
|
printk("Peer address type required\n");
|
|
return;
|
|
}
|
|
|
|
err = str2bt_addr_le(argv[1], argv[2], &addr);
|
|
if (err) {
|
|
printk("Invalid peer address (err %d)\n", err);
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_addr_le(&addr);
|
|
if (!conn) {
|
|
printk("Peer not connected\n");
|
|
return;
|
|
}
|
|
|
|
err = bt_gatt_exchange_mtu(conn, exchange_rsp);
|
|
if (err) {
|
|
printk("Exchange failed (err %d)\n", err);
|
|
} else {
|
|
printk("Exchange pending\n");
|
|
}
|
|
|
|
bt_conn_put(conn);
|
|
}
|
|
|
|
static const struct bt_eir ad[] = {
|
|
{
|
|
.len = 2,
|
|
.type = BT_EIR_FLAGS,
|
|
.data = { BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR },
|
|
},
|
|
{ }
|
|
};
|
|
|
|
static const struct bt_eir sd[] = {
|
|
{
|
|
.len = sizeof("test shell"),
|
|
.type = BT_EIR_NAME_COMPLETE,
|
|
.data = "test shell",
|
|
},
|
|
{ }
|
|
};
|
|
|
|
static void cmd_advertise(int argc, char *argv[])
|
|
{
|
|
if (bt_start_advertising(BT_LE_ADV_IND, ad, sd) < 0) {
|
|
printk("Failed to start advertising\n");
|
|
return;
|
|
}
|
|
|
|
printk("Advertising started\n");
|
|
}
|
|
|
|
static struct bt_gatt_discover_params discover_params;
|
|
static struct bt_uuid uuid;
|
|
|
|
static uint8_t discover_func(const struct bt_gatt_attr *attr, void *user_data)
|
|
{
|
|
printk("Discover found handle %u\n", attr->handle);
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
}
|
|
|
|
static void discover_destroy(void *user_data)
|
|
{
|
|
struct bt_gatt_discover_params *params = user_data;
|
|
|
|
printk("Discover destroy\n");
|
|
|
|
memset(params, 0, sizeof(*params));
|
|
}
|
|
|
|
static void cmd_gatt_discover(int argc, char *argv[])
|
|
{
|
|
int err;
|
|
bt_addr_le_t addr;
|
|
struct bt_conn *conn;
|
|
|
|
if (argc < 2) {
|
|
printk("Peer address required\n");
|
|
return;
|
|
}
|
|
|
|
if (argc < 3) {
|
|
printk("Peer address type required\n");
|
|
return;
|
|
}
|
|
|
|
err = str2bt_addr_le(argv[1], argv[2], &addr);
|
|
if (err) {
|
|
printk("Invalid peer address (err %d)\n", err);
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_addr_le(&addr);
|
|
if (!conn) {
|
|
printk("Peer not connected\n");
|
|
return;
|
|
}
|
|
|
|
discover_params.func = discover_func;
|
|
discover_params.destroy = discover_destroy;
|
|
discover_params.start_handle = 0x0001;
|
|
discover_params.end_handle = 0xffff;
|
|
|
|
if (argc < 4) {
|
|
if (!strcmp(argv[0], "gatt-discover")) {
|
|
printk("UUID type required\n");
|
|
return;
|
|
}
|
|
goto done;
|
|
}
|
|
|
|
/* Only set the UUID if the value is valid (non zero) */
|
|
uuid.u16 = xtoi(argv[3]);
|
|
if (uuid.u16) {
|
|
uuid.type = BT_UUID_16;
|
|
discover_params.uuid = &uuid;
|
|
}
|
|
|
|
if (argc > 4) {
|
|
discover_params.start_handle = xtoi(argv[4]);
|
|
if (argc > 5) {
|
|
discover_params.end_handle = xtoi(argv[5]);
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (!strcmp(argv[0], "gatt-discover-characteristic")) {
|
|
err = bt_gatt_discover_characteristic(conn, &discover_params);
|
|
} else {
|
|
err = bt_gatt_discover(conn, &discover_params);
|
|
}
|
|
|
|
if (err) {
|
|
printk("Discover failed (err %d)\n", err);
|
|
} else {
|
|
printk("Discover pending\n");
|
|
}
|
|
|
|
bt_conn_put(conn);
|
|
}
|
|
|
|
#ifdef CONFIG_MICROKERNEL
|
|
void mainloop(void)
|
|
#else
|
|
void main(void)
|
|
#endif
|
|
{
|
|
shell_init("btshell> ");
|
|
bt_conn_cb_register(&conn_callbacks);
|
|
|
|
shell_cmd_register("init", cmd_init);
|
|
shell_cmd_register("connect", cmd_connect_le);
|
|
shell_cmd_register("disconnect", cmd_disconnect);
|
|
shell_cmd_register("scan", cmd_scan);
|
|
shell_cmd_register("advertise", cmd_advertise);
|
|
shell_cmd_register("security", cmd_security);
|
|
shell_cmd_register("gatt-exchange-mtu", cmd_gatt_exchange_mtu);
|
|
shell_cmd_register("gatt-discover", cmd_gatt_discover);
|
|
shell_cmd_register("gatt-discover-characteristic", cmd_gatt_discover);
|
|
}
|