2023-03-23 10:44:58

by Simon Mikuda

[permalink] [raw]
Subject: [PATCH BlueZ 4/9] btgatt-client: Rewrite to use bt_shell

---
Makefile.tools | 2 +-
src/shared/shell.h | 1 +
tools/btgatt-client.c | 1087 ++++++++++++++++-------------------------
3 files changed, 412 insertions(+), 678 deletions(-)

diff --git a/Makefile.tools b/Makefile.tools
index df4cad065..6125e57f7 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -315,7 +315,7 @@ tools_ibeacon_LDADD = src/libshared-mainloop.la

tools_btgatt_client_SOURCES = tools/btgatt-client.c src/uuid-helper.c
tools_btgatt_client_LDADD = src/libshared-mainloop.la \
- lib/libbluetooth-internal.la
+ lib/libbluetooth-internal.la -lreadline

tools_btgatt_server_SOURCES = tools/btgatt-server.c src/uuid-helper.c
tools_btgatt_server_LDADD = src/libshared-mainloop.la \
diff --git a/src/shared/shell.h b/src/shared/shell.h
index 87fb5c415..8793835c0 100644
--- a/src/shared/shell.h
+++ b/src/shared/shell.h
@@ -15,6 +15,7 @@
#define COLOR_GREEN "\001\x1B[0;92m\002"
#define COLOR_YELLOW "\001\x1B[0;93m\002"
#define COLOR_BLUE "\001\x1B[0;94m\002"
+#define COLOR_MAGENTA "\x1B[0;95m"
#define COLOR_BOLDGRAY "\001\x1B[1;30m\002"
#define COLOR_BOLDWHITE "\001\x1B[1;37m\002"
#define COLOR_HIGHLIGHT "\001\x1B[1;39m\002"
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 2a0cb5181..ecfe3f3f1 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -28,6 +28,7 @@
#include "lib/uuid.h"

#include "src/shared/mainloop.h"
+#include "src/shared/shell.h"
#include "src/shared/util.h"
#include "src/shared/att.h"
#include "src/shared/queue.h"
@@ -37,19 +38,29 @@
#define ATT_CID 4
#define ATT_PSM 31

-#define PRLOG(...) \
- printf(__VA_ARGS__); print_prompt();
-
-#define COLOR_OFF "\x1B[0m"
-#define COLOR_RED "\x1B[0;91m"
-#define COLOR_GREEN "\x1B[0;92m"
-#define COLOR_YELLOW "\x1B[0;93m"
-#define COLOR_BLUE "\x1B[0;94m"
-#define COLOR_MAGENTA "\x1B[0;95m"
-#define COLOR_BOLDGRAY "\x1B[1;30m"
-#define COLOR_BOLDWHITE "\x1B[1;37m"
+#define MAX_LEN_LINE 512

+struct client *cli;
static bool verbose = false;
+static bool shell_running = false;
+
+#define print(fmt, arg...) do { \
+ if (shell_running) \
+ bt_shell_printf(fmt "\n", ## arg); \
+ else \
+ printf(fmt "\n", ## arg); \
+} while (0)
+
+#define error(fmt, arg...) do { \
+ if (shell_running) \
+ bt_shell_printf(COLOR_RED fmt "\n" COLOR_OFF, ## arg); \
+ else \
+ fprintf(stderr, COLOR_RED fmt "\n" COLOR_OFF, ## arg); \
+} while (0)
+
+#define append(str, fmt, arg...) do { \
+ sprintf(strchr(str, '\0'), fmt, ## arg); \
+} while (0)

struct client {
int fd;
@@ -60,10 +71,11 @@ struct client {
unsigned int reliable_session_id;
};

-static void print_prompt(void)
+static void update_prompt(void)
{
- printf(COLOR_BLUE "[GATT client]" COLOR_OFF "# ");
- fflush(stdout);
+ char str[32];
+ sprintf(str, COLOR_BLUE "[GATT client]" COLOR_OFF "# ");
+ bt_shell_set_prompt(str);
}

static const char *ecode_to_string(uint8_t ecode)
@@ -116,7 +128,7 @@ static const char *ecode_to_string(uint8_t ecode)

static void att_disconnect_cb(int err, void *user_data)
{
- printf("Device disconnected: %s\n", strerror(err));
+ print("Device disconnected: %s", strerror(err));

mainloop_quit();
}
@@ -125,14 +137,14 @@ static void att_debug_cb(const char *str, void *user_data)
{
const char *prefix = user_data;

- PRLOG(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s\n" COLOR_OFF, prefix, str);
+ print(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s" COLOR_OFF, prefix, str);
}

static void gatt_debug_cb(const char *str, void *user_data)
{
const char *prefix = user_data;

- PRLOG(COLOR_GREEN "%s%s\n" COLOR_OFF, prefix, str);
+ print(COLOR_GREEN "%s%s" COLOR_OFF, prefix, str);
}

static void ready_cb(bool success, uint8_t att_ecode, void *user_data);
@@ -150,7 +162,7 @@ static void log_service_event(struct gatt_db_attribute *attr, const char *str)

gatt_db_attribute_get_service_handles(attr, &start, &end);

- PRLOG("%s - UUID: %s start: 0x%04x end: 0x%04x\n", str, uuid_str,
+ print("%s - UUID: %s start: 0x%04x end: 0x%04x", str, uuid_str,
start, end);
}

@@ -170,20 +182,20 @@ static struct client *client_create(int fd, uint16_t mtu)

cli = new0(struct client, 1);
if (!cli) {
- fprintf(stderr, "Failed to allocate memory for client\n");
+ error("Failed to allocate memory for client");
return NULL;
}

cli->att = bt_att_new(fd, false);
if (!cli->att) {
- fprintf(stderr, "Failed to initialze ATT transport layer\n");
+ error("Failed to initialze ATT transport layer");
bt_att_unref(cli->att);
free(cli);
return NULL;
}

if (!bt_att_set_close_on_unref(cli->att, true)) {
- fprintf(stderr, "Failed to set up ATT transport layer\n");
+ error("Failed to set up ATT transport layer");
bt_att_unref(cli->att);
free(cli);
return NULL;
@@ -191,7 +203,7 @@ static struct client *client_create(int fd, uint16_t mtu)

if (!bt_att_register_disconnect(cli->att, att_disconnect_cb, NULL,
NULL)) {
- fprintf(stderr, "Failed to set ATT disconnect handler\n");
+ error("Failed to set ATT disconnect handler");
bt_att_unref(cli->att);
free(cli);
return NULL;
@@ -200,7 +212,7 @@ static struct client *client_create(int fd, uint16_t mtu)
cli->fd = fd;
cli->db = gatt_db_new();
if (!cli->db) {
- fprintf(stderr, "Failed to create GATT database\n");
+ error("Failed to create GATT database");
bt_att_unref(cli->att);
free(cli);
return NULL;
@@ -208,7 +220,7 @@ static struct client *client_create(int fd, uint16_t mtu)

cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu, 0);
if (!cli->gatt) {
- fprintf(stderr, "Failed to create GATT client\n");
+ error("Failed to create GATT client");
gatt_db_unref(cli->db);
bt_att_unref(cli->att);
free(cli);
@@ -225,8 +237,8 @@ static struct client *client_create(int fd, uint16_t mtu)
NULL);
}

- bt_gatt_client_ready_register(cli->gatt, ready_cb, cli, NULL);
- bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, cli,
+ bt_gatt_client_ready_register(cli->gatt, ready_cb, NULL, NULL);
+ bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, NULL,
NULL);

/* bt_gatt_client already holds a reference */
@@ -242,7 +254,7 @@ static void client_destroy(struct client *cli)
free(cli);
}

-static void print_uuid(const bt_uuid_t *uuid)
+static void append_uuid(char *str, const bt_uuid_t *uuid)
{
char uuid_str[MAX_LEN_UUID_STR];
bt_uuid_t uuid128;
@@ -250,15 +262,15 @@ static void print_uuid(const bt_uuid_t *uuid)
bt_uuid_to_uuid128(uuid, &uuid128);
bt_uuid_to_string(&uuid128, uuid_str, sizeof(uuid_str));

- printf("%s\n", uuid_str);
+ append(str, "%s", uuid_str);
}

static void print_incl(struct gatt_db_attribute *attr, void *user_data)
{
- struct client *cli = user_data;
uint16_t handle, start, end;
struct gatt_db_attribute *service;
bt_uuid_t uuid;
+ char line[MAX_LEN_LINE] = {0};

if (!gatt_db_attribute_get_incl_data(attr, &handle, &start, &end))
return;
@@ -269,18 +281,21 @@ static void print_incl(struct gatt_db_attribute *attr, void *user_data)

gatt_db_attribute_get_service_uuid(service, &uuid);

- printf("\t " COLOR_GREEN "include" COLOR_OFF " - handle: "
+ append(line, "\t " COLOR_GREEN "include" COLOR_OFF " - handle: "
"0x%04x, - start: 0x%04x, end: 0x%04x,"
"uuid: ", handle, start, end);
- print_uuid(&uuid);
+ append_uuid(line, &uuid);
+ print("%s", line);
}

static void print_desc(struct gatt_db_attribute *attr, void *user_data)
{
- printf("\t\t " COLOR_MAGENTA "descr" COLOR_OFF
+ char line[MAX_LEN_LINE] = {0};
+ append(line, "\t\t " COLOR_MAGENTA "descr" COLOR_OFF
" - handle: 0x%04x, uuid: ",
gatt_db_attribute_get_handle(attr));
- print_uuid(gatt_db_attribute_get_type(attr));
+ append_uuid(line, gatt_db_attribute_get_type(attr));
+ print("%s", line);
}

static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
@@ -289,6 +304,7 @@ static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
uint8_t properties;
uint16_t ext_prop;
bt_uuid_t uuid;
+ char line[MAX_LEN_LINE] = {0};

if (!gatt_db_attribute_get_char_data(attr, &handle,
&value_handle,
@@ -297,170 +313,133 @@ static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
&uuid))
return;

- printf("\t " COLOR_YELLOW "charac" COLOR_OFF
+ append(line, "\t " COLOR_YELLOW "charac" COLOR_OFF
" - start: 0x%04x, value: 0x%04x, "
"props: 0x%02x, ext_props: 0x%04x, uuid: ",
handle, value_handle, properties, ext_prop);
- print_uuid(&uuid);
+ append_uuid(line, &uuid);
+ print("%s", line);

gatt_db_service_foreach_desc(attr, print_desc, NULL);
}

static void print_service(struct gatt_db_attribute *attr, void *user_data)
{
- struct client *cli = user_data;
uint16_t start, end;
bool primary;
bt_uuid_t uuid;
+ char line[MAX_LEN_LINE] = {0};

if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary,
&uuid))
return;

- printf(COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
+ append(line, COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
"end: 0x%04x, type: %s, uuid: ",
start, end, primary ? "primary" : "secondary");
- print_uuid(&uuid);
+ append_uuid(line, &uuid);
+ print("%s", line);

- gatt_db_service_foreach_incl(attr, print_incl, cli);
+ gatt_db_service_foreach_incl(attr, print_incl, NULL);
gatt_db_service_foreach_char(attr, print_chrc, NULL);
-
- printf("\n");
}

static void print_services(struct client *cli)
{
- printf("\n");
-
- gatt_db_foreach_service(cli->db, NULL, print_service, cli);
+ gatt_db_foreach_service(cli->db, NULL, print_service, NULL);
}

-static void print_services_by_uuid(struct client *cli, const bt_uuid_t *uuid)
+static void print_services_by_uuid(const bt_uuid_t *uuid)
{
- printf("\n");
-
- gatt_db_foreach_service(cli->db, uuid, print_service, cli);
+ gatt_db_foreach_service(cli->db, uuid, print_service, NULL);
}

-static void print_services_by_handle(struct client *cli, uint16_t handle)
+static void print_services_by_handle(uint16_t handle)
{
- printf("\n");
-
/* TODO: Filter by handle */
gatt_db_foreach_service(cli->db, NULL, print_service, cli);
}

static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
{
- struct client *cli = user_data;
-
if (!success) {
- PRLOG("GATT discovery procedures failed - error code: 0x%02x\n",
+ error("GATT discovery procedures failed - error code: 0x%02x",
att_ecode);
return;
}

- PRLOG("GATT discovery procedures complete\n");
+ print("GATT discovery procedures complete");

print_services(cli);
- print_prompt();
}

static void service_changed_cb(uint16_t start_handle, uint16_t end_handle,
void *user_data)
{
- struct client *cli = user_data;
-
- printf("\nService Changed handled - start: 0x%04x end: 0x%04x\n",
+ print("Service Changed handled - start: 0x%04x end: 0x%04x",
start_handle, end_handle);

- gatt_db_foreach_service_in_range(cli->db, NULL, print_service, cli,
+ gatt_db_foreach_service_in_range(cli->db, NULL, print_service, NULL,
start_handle, end_handle);
- print_prompt();
-}
-
-static void services_usage(void)
-{
- printf("Usage: services [options]\nOptions:\n"
- "\t -u, --uuid <uuid>\tService UUID\n"
- "\t -a, --handle <handle>\tService start handle\n"
- "\t -h, --help\t\tShow help message\n"
- "e.g.:\n"
- "\tservices\n\tservices -u 0x180d\n\tservices -a 0x0009\n");
}

-static bool parse_args(char *str, int expected_argc, char **argv, int *argc)
-{
- char **ap;
-
- for (ap = argv; (*ap = strsep(&str, " \t")) != NULL;) {
- if (**ap == '\0')
- continue;
-
- (*argc)++;
- ap++;
-
- if (*argc > expected_argc)
- return false;
- }
-
- return true;
-}
+static struct option services_options[] = {
+ { "uuid", 1, 0, 'u' },
+ { "handle", 1, 0, 'a' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};

-static void cmd_services(struct client *cli, char *cmd_str)
+static void cmd_services(int argc, char **argv)
{
- char *argv[3];
- int argc = 0;
+ int opt;
+ bool use_uuid = false;
+ bt_uuid_t tmp, uuid;
+ uint16_t handle = 0;
+ char *endptr = NULL;

if (!bt_gatt_client_is_ready(cli->gatt)) {
- printf("GATT client not initialized\n");
- return;
- }
-
- if (!parse_args(cmd_str, 2, argv, &argc)) {
- services_usage();
- return;
- }
-
- if (!argc) {
- print_services(cli);
+ print("GATT client not initialized");
return;
}

- if (argc != 2) {
- services_usage();
- return;
- }
-
- if (!strcmp(argv[0], "-u") || !strcmp(argv[0], "--uuid")) {
- bt_uuid_t tmp, uuid;
-
- if (bt_string_to_uuid(&tmp, argv[1]) < 0) {
- printf("Invalid UUID: %s\n", argv[1]);
- return;
- }
-
- bt_uuid_to_uuid128(&tmp, &uuid);
-
- print_services_by_uuid(cli, &uuid);
- } else if (!strcmp(argv[0], "-a") || !strcmp(argv[0], "--handle")) {
- uint16_t handle;
- char *endptr = NULL;
-
- handle = strtol(argv[1], &endptr, 0);
- if (!endptr || *endptr != '\0') {
- printf("Invalid start handle: %s\n", argv[1]);
- return;
+ while ((opt = getopt_long(argc, argv, "u:a:", services_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 'u':
+ if (bt_string_to_uuid(&tmp, optarg) < 0) {
+ error("Invalid UUID: %s", optarg);
+ optind = 0;
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+ bt_uuid_to_uuid128(&tmp, &uuid);
+ use_uuid = true;
+ break;
+ case 'a':
+ handle = strtol(optarg, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ error("Invalid start handle: %s", optarg);
+ optind = 0;
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+ break;
+ case 'h':
+ bt_shell_usage();
+ optind = 0;
+ return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+ default:
+ bt_shell_usage();
+ optind = 0;
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
+ }

- print_services_by_handle(cli, handle);
- } else
- services_usage();
-}
+ optind = 0;

-static void read_multiple_usage(void)
-{
- printf("Usage: read-multiple <handle_1> <handle_2> ...\n");
+ if (use_uuid)
+ print_services_by_uuid(&uuid);
+ else
+ print_services_by_handle(handle);
}

static void read_multiple_cb(bool success, uint8_t att_ecode,
@@ -468,43 +447,37 @@ static void read_multiple_cb(bool success, uint8_t att_ecode,
void *user_data)
{
int i;
+ char line[MAX_LEN_LINE] = {0};

if (!success) {
- PRLOG("\nRead multiple request failed: 0x%02x\n", att_ecode);
+ error("Read multiple request failed: 0x%02x", att_ecode);
return;
}

- printf("\nRead multiple value (%u bytes):", length);
+ append(line, "Read multiple value (%u bytes):", length);

for (i = 0; i < length; i++)
- printf("%02x ", value[i]);
+ append(line, "%02x ", value[i]);

- PRLOG("\n");
+ print("%s", line);
}

-static void cmd_read_multiple(struct client *cli, char *cmd_str)
+static void cmd_read_multiple(int argc, char **argv)
{
- int argc = 0;
uint16_t *value;
- char *argv[512];
int i;
char *endptr = NULL;

- if (!parse_args(cmd_str, sizeof(argv), argv, &argc) || argc < 2) {
- read_multiple_usage();
- return;
- }
-
value = malloc(sizeof(uint16_t) * argc);
if (!value) {
- printf("Failed to construct value\n");
+ error("Failed to construct value");
return;
}

- for (i = 0; i < argc; i++) {
+ for (i = 1; i < argc; i++) {
value[i] = strtol(argv[i], &endptr, 0);
if (endptr == argv[i] || *endptr != '\0' || !value[i]) {
- printf("Invalid value byte: %s\n", argv[i]);
+ error("Invalid value byte: %s", argv[i]);
free(value);
return;
}
@@ -512,133 +485,98 @@ static void cmd_read_multiple(struct client *cli, char *cmd_str)

if (!bt_gatt_client_read_multiple(cli->gatt, value, argc,
read_multiple_cb, NULL, NULL))
- printf("Failed to initiate read multiple procedure\n");
+ error("Failed to initiate read multiple procedure");

free(value);
}

-static void read_value_usage(void)
-{
- printf("Usage: read-value <value_handle>\n");
-}
-
static void read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
uint16_t length, void *user_data)
{
int i;
+ char line[MAX_LEN_LINE] = {0};

if (!success) {
- PRLOG("\nRead request failed: %s (0x%02x)\n",
+ error("Read request failed: %s (0x%02x)",
ecode_to_string(att_ecode), att_ecode);
return;
}

- printf("\nRead value");
+ append(line, "Read value");

if (length == 0) {
- PRLOG(": 0 bytes\n");
+ print("%s: 0 bytes", line);
return;
}

- printf(" (%u bytes): ", length);
+ append(line, " (%u bytes): ", length);

for (i = 0; i < length; i++)
- printf("%02x ", value[i]);
+ append(line, "%02x ", value[i]);

- PRLOG("\n");
+ print("%s", line);
}

-static void cmd_read_value(struct client *cli, char *cmd_str)
+static void cmd_read_value(int argc, char **argv)
{
- char *argv[2];
- int argc = 0;
uint16_t handle;
char *endptr = NULL;

- if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) {
- read_value_usage();
- return;
- }
-
- handle = strtol(argv[0], &endptr, 0);
+ handle = strtol(argv[1], &endptr, 0);
if (!endptr || *endptr != '\0' || !handle) {
- printf("Invalid value handle: %s\n", argv[0]);
+ error("Invalid value handle: %s", argv[1]);
return;
}

if (!bt_gatt_client_read_value(cli->gatt, handle, read_cb,
NULL, NULL))
- printf("Failed to initiate read value procedure\n");
-}
-
-static void read_long_value_usage(void)
-{
- printf("Usage: read-long-value <value_handle> <offset>\n");
+ error("Failed to initiate read value procedure");
}

-static void cmd_read_long_value(struct client *cli, char *cmd_str)
+static void cmd_read_long_value(int argc, char **argv)
{
- char *argv[3];
- int argc = 0;
uint16_t handle;
uint16_t offset;
char *endptr = NULL;

- if (!parse_args(cmd_str, 2, argv, &argc) || argc != 2) {
- read_long_value_usage();
- return;
- }
-
- handle = strtol(argv[0], &endptr, 0);
+ handle = strtol(argv[1], &endptr, 0);
if (!endptr || *endptr != '\0' || !handle) {
- printf("Invalid value handle: %s\n", argv[0]);
+ error("Invalid value handle: %s", argv[1]);
return;
}

endptr = NULL;
- offset = strtol(argv[1], &endptr, 0);
+ offset = strtol(argv[2], &endptr, 0);
if (!endptr || *endptr != '\0') {
- printf("Invalid offset: %s\n", argv[1]);
+ error("Invalid offset: %s", argv[2]);
return;
}

if (!bt_gatt_client_read_long_value(cli->gatt, handle, offset, read_cb,
NULL, NULL))
- printf("Failed to initiate read long value procedure\n");
-}
-
-static void write_value_usage(void)
-{
- printf("Usage: write-value [options] <value_handle> <value>\n"
- "Options:\n"
- "\t-w, --without-response\tWrite without response\n"
- "\t-s, --signed-write\tSigned write command\n"
- "e.g.:\n"
- "\twrite-value 0x0001 00 01 00\n");
+ error("Failed to initiate read long value procedure");
}

static struct option write_value_options[] = {
{ "without-response", 0, 0, 'w' },
{ "signed-write", 0, 0, 's' },
+ { "help", 0, 0, 'h' },
{ }
};

static void write_cb(bool success, uint8_t att_ecode, void *user_data)
{
if (success) {
- PRLOG("\nWrite successful\n");
+ print("Write successful");
} else {
- PRLOG("\nWrite failed: %s (0x%02x)\n",
+ error("Write failed: %s (0x%02x)",
ecode_to_string(att_ecode), att_ecode);
}
}

-static void cmd_write_value(struct client *cli, char *cmd_str)
+static void cmd_write_value(int argc, char **argv)
{
int opt, i, val;
- char *argvbuf[516];
- char **argv = argvbuf;
- int argc = 1;
uint16_t handle;
char *endptr = NULL;
int length;
@@ -646,14 +584,6 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
bool without_response = false;
bool signed_write = false;

- if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
- printf("Too many arguments\n");
- write_value_usage();
- return;
- }
-
- optind = 0;
- argv[0] = "write-value";
while ((opt = getopt_long(argc, argv, "+ws", write_value_options,
NULL)) != -1) {
switch (opt) {
@@ -663,23 +593,24 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
case 's':
signed_write = true;
break;
+ case 'h':
+ bt_shell_usage();
+ optind = 0;
+ return bt_shell_noninteractive_quit(EXIT_SUCCESS);
default:
- write_value_usage();
- return;
+ bt_shell_usage();
+ optind = 0;
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
}

argc -= optind;
argv += optind;
-
- if (argc < 1) {
- write_value_usage();
- return;
- }
+ optind = 0;

handle = strtol(argv[0], &endptr, 0);
if (!endptr || *endptr != '\0' || !handle) {
- printf("Invalid handle: %s\n", argv[0]);
+ error("Invalid handle: %s", argv[1]);
return;
}

@@ -687,13 +618,13 @@ static void cmd_write_value(struct client *cli, char *cmd_str)

if (length > 0) {
if (length > UINT16_MAX) {
- printf("Write value too long\n");
+ error("Write value too long");
return;
}

value = malloc(length);
if (!value) {
- printf("Failed to construct write value\n");
+ error("Failed to construct write value");
return;
}

@@ -701,7 +632,7 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
val = strtol(argv[i], &endptr, 0);
if (endptr == argv[i] || *endptr != '\0'
|| errno == ERANGE || val < 0 || val > 255) {
- printf("Invalid value byte: %s\n",
+ error("Invalid value byte: %s",
argv[i]);
goto done;
}
@@ -712,36 +643,27 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
if (without_response) {
if (!bt_gatt_client_write_without_response(cli->gatt, handle,
signed_write, value, length)) {
- printf("Failed to initiate write without response "
- "procedure\n");
+ error("Failed to initiate write without response "
+ "procedure");
goto done;
}

- printf("Write command sent\n");
+ print("Write command sent");
goto done;
}

if (!bt_gatt_client_write_value(cli->gatt, handle, value, length,
write_cb,
NULL, NULL))
- printf("Failed to initiate write procedure\n");
+ error("Failed to initiate write procedure");

done:
free(value);
}

-static void write_long_value_usage(void)
-{
- printf("Usage: write-long-value [options] <value_handle> <offset> "
- "<value>\n"
- "Options:\n"
- "\t-r, --reliable-write\tReliable write\n"
- "e.g.:\n"
- "\twrite-long-value 0x0001 0 00 01 00\n");
-}
-
static struct option write_long_value_options[] = {
{ "reliable-write", 0, 0, 'r' },
+ { "help", 0, 0, 'h' },
{ }
};

@@ -749,21 +671,18 @@ static void write_long_cb(bool success, bool reliable_error, uint8_t att_ecode,
void *user_data)
{
if (success) {
- PRLOG("Write successful\n");
+ print("Write successful");
} else if (reliable_error) {
- PRLOG("Reliable write not verified\n");
+ error("Reliable write not verified");
} else {
- PRLOG("\nWrite failed: %s (0x%02x)\n",
+ error("Write failed: %s (0x%02x)",
ecode_to_string(att_ecode), att_ecode);
}
}

-static void cmd_write_long_value(struct client *cli, char *cmd_str)
+static void cmd_write_long_value(int argc, char **argv)
{
int opt, i, val;
- char *argvbuf[516];
- char **argv = argvbuf;
- int argc = 1;
uint16_t handle;
uint16_t offset;
char *endptr = NULL;
@@ -771,44 +690,44 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)
uint8_t *value = NULL;
bool reliable_writes = false;

- if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
- printf("Too many arguments\n");
- write_value_usage();
- return;
- }
-
- optind = 0;
- argv[0] = "write-long-value";
while ((opt = getopt_long(argc, argv, "+r", write_long_value_options,
NULL)) != -1) {
switch (opt) {
case 'r':
reliable_writes = true;
break;
+ case 'h':
+ bt_shell_usage();
+ optind = 0;
+ return bt_shell_noninteractive_quit(EXIT_SUCCESS);
default:
- write_long_value_usage();
- return;
+ bt_shell_usage();
+ optind = 0;
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
}

argc -= optind;
argv += optind;
+ optind = 0;

- if (argc < 2) {
- write_long_value_usage();
+ if (argc > 514) {
+ error("Too many arguments");
+ bt_shell_usage();
+ optind = 0;
return;
}

handle = strtol(argv[0], &endptr, 0);
if (!endptr || *endptr != '\0' || !handle) {
- printf("Invalid handle: %s\n", argv[0]);
+ error("Invalid handle: %s", argv[1]);
return;
}

endptr = NULL;
offset = strtol(argv[1], &endptr, 0);
if (!endptr || *endptr != '\0' || errno == ERANGE) {
- printf("Invalid offset: %s\n", argv[1]);
+ error("Invalid offset: %s", argv[2]);
return;
}

@@ -816,13 +735,13 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)

if (length > 0) {
if (length > UINT16_MAX) {
- printf("Write value too long\n");
+ error("Write value too long");
return;
}

value = malloc(length);
if (!value) {
- printf("Failed to construct write value\n");
+ error("Failed to construct write value");
return;
}

@@ -830,7 +749,7 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)
val = strtol(argv[i], &endptr, 0);
if (endptr == argv[i] || *endptr != '\0'
|| errno == ERANGE || val < 0 || val > 255) {
- printf("Invalid value byte: %s\n",
+ error("Invalid value byte: %s",
argv[i]);
free(value);
return;
@@ -843,32 +762,20 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)
offset, value, length,
write_long_cb,
NULL, NULL))
- printf("Failed to initiate long write procedure\n");
+ error("Failed to initiate long write procedure");

free(value);
}

-static void write_prepare_usage(void)
-{
- printf("Usage: write-prepare [options] <value_handle> <offset> "
- "<value>\n"
- "Options:\n"
- "\t-s, --session-id\tSession id\n"
- "e.g.:\n"
- "\twrite-prepare -s 1 0x0001 00 01 00\n");
-}
-
static struct option write_prepare_options[] = {
{ "session-id", 1, 0, 's' },
+ { "help", 0, 0, 'h' },
{ }
};

-static void cmd_write_prepare(struct client *cli, char *cmd_str)
+static void cmd_write_prepare(int argc, char **argv)
{
int opt, i, val;
- char *argvbuf[516];
- char **argv = argvbuf;
- int argc = 0;
unsigned int id = 0;
uint16_t handle;
uint16_t offset;
@@ -876,59 +783,50 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str)
unsigned int length;
uint8_t *value = NULL;

- if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
- printf("Too many arguments\n");
- write_value_usage();
- return;
- }
-
- /* Add command name for getopt_long */
- argc++;
- argv[0] = "write-prepare";
-
- optind = 0;
while ((opt = getopt_long(argc, argv , "s:", write_prepare_options,
NULL)) != -1) {
switch (opt) {
case 's':
- if (!optarg) {
- write_prepare_usage();
- return;
- }
-
id = atoi(optarg);
-
break;
+ case 'h':
+ bt_shell_usage();
+ optind = 0;
+ return bt_shell_noninteractive_quit(EXIT_SUCCESS);
default:
- write_prepare_usage();
- return;
+ bt_shell_usage();
+ optind = 0;
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
}

argc -= optind;
argv += optind;
+ optind = 0;

- if (argc < 3) {
- write_prepare_usage();
+ if (argc > 514) {
+ error("Too many arguments");
+ bt_shell_usage();
+ optind = 0;
return;
}

if (cli->reliable_session_id != id) {
- printf("Session id != Ongoing session id (%u!=%u)\n", id,
+ error("Session id != Ongoing session id (%u!=%u)", id,
cli->reliable_session_id);
return;
}

handle = strtol(argv[0], &endptr, 0);
if (!endptr || *endptr != '\0' || !handle) {
- printf("Invalid handle: %s\n", argv[0]);
+ error("Invalid handle: %s", argv[1]);
return;
}

endptr = NULL;
offset = strtol(argv[1], &endptr, 0);
if (!endptr || *endptr != '\0' || errno == ERANGE) {
- printf("Invalid offset: %s\n", argv[1]);
+ error("Invalid offset: %s", argv[2]);
return;
}

@@ -942,13 +840,13 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str)
goto done;

if (length > UINT16_MAX) {
- printf("Write value too long\n");
+ error("Write value too long");
return;
}

value = malloc(length);
if (!value) {
- printf("Failed to allocate memory for value\n");
+ error("Failed to allocate memory for value");
return;
}

@@ -956,7 +854,7 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str)
val = strtol(argv[i], &endptr, 0);
if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE
|| val < 0 || val > 255) {
- printf("Invalid value byte: %s\n", argv[i]);
+ error("Invalid value byte: %s", argv[i]);
free(value);
return;
}
@@ -971,64 +869,43 @@ done:
write_long_cb, NULL,
NULL);
if (!cli->reliable_session_id)
- printf("Failed to proceed prepare write\n");
+ error("Failed to proceed prepare write");
else
- printf("Prepare write success.\n"
- "Session id: %d to be used on next write\n",
+ print("Prepare write success."
+ "Session id: %d to be used on next write",
cli->reliable_session_id);

free(value);
}

-static void write_execute_usage(void)
-{
- printf("Usage: write-execute <session_id> <execute>\n"
- "e.g.:\n"
- "\twrite-execute 1 0\n");
-}
-
-static void cmd_write_execute(struct client *cli, char *cmd_str)
+static void cmd_write_execute(int argc, char **argv)
{
- char *argvbuf[516];
- char **argv = argvbuf;
- int argc = 0;
char *endptr = NULL;
unsigned int session_id;
bool execute;

- if (!parse_args(cmd_str, 514, argv, &argc)) {
- printf("Too many arguments\n");
- write_value_usage();
- return;
- }
-
- if (argc < 2) {
- write_execute_usage();
- return;
- }
-
- session_id = strtol(argv[0], &endptr, 0);
+ session_id = strtol(argv[1], &endptr, 0);
if (!endptr || *endptr != '\0') {
- printf("Invalid session id: %s\n", argv[0]);
+ error("Invalid session id: %s", argv[1]);
return;
}

if (session_id != cli->reliable_session_id) {
- printf("Invalid session id: %u != %u\n", session_id,
+ error("Invalid session id: %u != %u", session_id,
cli->reliable_session_id);
return;
}

- execute = !!strtol(argv[1], &endptr, 0);
+ execute = !!strtol(argv[2], &endptr, 0);
if (!endptr || *endptr != '\0') {
- printf("Invalid execute: %s\n", argv[1]);
+ error("Invalid execute: %s", argv[2]);
return;
}

if (execute) {
if (!bt_gatt_client_write_execute(cli->gatt, session_id,
write_cb, NULL, NULL))
- printf("Failed to proceed write execute\n");
+ error("Failed to proceed write execute");
} else {
bt_gatt_client_cancel(cli->gatt, session_id);
}
@@ -1036,46 +913,40 @@ static void cmd_write_execute(struct client *cli, char *cmd_str)
cli->reliable_session_id = 0;
}

-static void register_notify_usage(void)
-{
- printf("Usage: register-notify <chrc value handle>\n");
-}
-
static void notify_cb(uint16_t value_handle, const uint8_t *value,
uint16_t length, void *user_data)
{
int i;
+ char line[MAX_LEN_LINE] = {0};

- printf("\n\tHandle Value Not/Ind: 0x%04x - ", value_handle);
+ append(line, "\tHandle Value Not/Ind: 0x%04x - ", value_handle);

if (length == 0) {
- PRLOG("(0 bytes)\n");
+ print("%s(0 bytes)", line);
return;
}

- printf("(%u bytes): ", length);
+ append(line, "(%u bytes): ", length);

for (i = 0; i < length; i++)
- printf("%02x ", value[i]);
+ append(line, "%02x ", value[i]);

- PRLOG("\n");
+ print("%s", line);
}

static void register_notify_cb(uint16_t att_ecode, void *user_data)
{
if (att_ecode) {
- PRLOG("Failed to register notify handler "
- "- error code: 0x%02x\n", att_ecode);
+ error("Failed to register notify handler "
+ "- error code: 0x%02x", att_ecode);
return;
}

- PRLOG("Registered notify handler!\n");
+ print("Registered notify handler!");
}

-static void cmd_register_notify(struct client *cli, char *cmd_str)
+static void cmd_register_notify(int argc, char **argv)
{
- char *argv[2];
- int argc = 0;
uint16_t value_handle;
unsigned int id;
char *endptr = NULL;
@@ -1085,14 +956,9 @@ static void cmd_register_notify(struct client *cli, char *cmd_str)
return;
}

- if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) {
- register_notify_usage();
- return;
- }
-
- value_handle = strtol(argv[0], &endptr, 0);
+ value_handle = strtol(argv[1], &endptr, 0);
if (!endptr || *endptr != '\0' || !value_handle) {
- printf("Invalid value handle: %s\n", argv[0]);
+ error("Invalid value handle: %s", argv[1]);
return;
}

@@ -1100,22 +966,15 @@ static void cmd_register_notify(struct client *cli, char *cmd_str)
register_notify_cb,
notify_cb, NULL, NULL);
if (!id) {
- printf("Failed to register notify handler\n");
+ error("Failed to register notify handler");
return;
}

- printf("Registering notify handler with id: %u\n", id);
+ print("Registering notify handler with id: %u", id);
}

-static void unregister_notify_usage(void)
+static void cmd_unregister_notify(int argc, char **argv)
{
- printf("Usage: unregister-notify <notify id>\n");
-}
-
-static void cmd_unregister_notify(struct client *cli, char *cmd_str)
-{
- char *argv[2];
- int argc = 0;
unsigned int id;
char *endptr = NULL;

@@ -1124,72 +983,46 @@ static void cmd_unregister_notify(struct client *cli, char *cmd_str)
return;
}

- if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) {
- unregister_notify_usage();
- return;
- }
-
- id = strtol(argv[0], &endptr, 0);
+ id = strtol(argv[1], &endptr, 0);
if (!endptr || *endptr != '\0' || !id) {
- printf("Invalid notify id: %s\n", argv[0]);
+ error("Invalid notify id: %s", argv[1]);
return;
}

if (!bt_gatt_client_unregister_notify(cli->gatt, id)) {
- printf("Failed to unregister notify handler with id: %u\n", id);
+ error("Failed to unregister notify handler with id: %u", id);
return;
}

- printf("Unregistered notify handler with id: %u\n", id);
-}
-
-static void set_security_usage(void)
-{
- printf("Usage: set-security <level>\n"
- "level: 1-3\n"
- "e.g.:\n"
- "\tset-security 2\n");
+ print("Unregistered notify handler with id: %u", id);
}

-static void cmd_set_security(struct client *cli, char *cmd_str)
+static void cmd_set_security(int argc, char **argv)
{
- char *argv[2];
- int argc = 0;
char *endptr = NULL;
int level;

- if (!parse_args(cmd_str, 1, argv, &argc)) {
- printf("Too many arguments\n");
- set_security_usage();
- return;
- }
-
- if (argc < 1) {
- set_security_usage();
- return;
- }
-
- level = strtol(argv[0], &endptr, 0);
+ level = strtol(argv[1], &endptr, 0);
if (!endptr || *endptr != '\0' || level < 1 || level > 3) {
- printf("Invalid level: %s\n", argv[0]);
+ error("Invalid level: %s", argv[1]);
return;
}

if (!bt_gatt_client_set_security(cli->gatt, level))
- printf("Could not set sec level\n");
+ error("Could not set sec level");
else
- printf("Setting security level %d success\n", level);
+ print("Setting security level %d success", level);
}

-static void cmd_get_security(struct client *cli, char *cmd_str)
+static void cmd_get_security(int argc, char **argv)
{
int level;

level = bt_gatt_client_get_security(cli->gatt);
if (level < 0)
- printf("Could not set sec level\n");
+ error("Could not get sec level");
else
- printf("Security level: %u\n", level);
+ print("Security level: %u", level);
}

static bool convert_sign_key(char *optarg, uint8_t key[16])
@@ -1197,7 +1030,7 @@ static bool convert_sign_key(char *optarg, uint8_t key[16])
int i;

if (strlen(optarg) != 32) {
- printf("sign-key length is invalid\n");
+ error("sign-key length is invalid");
return false;
}

@@ -1209,14 +1042,6 @@ static bool convert_sign_key(char *optarg, uint8_t key[16])
return true;
}

-static void set_sign_key_usage(void)
-{
- printf("Usage: set-sign-key [options]\nOptions:\n"
- "\t -c, --sign-key <csrk>\tCSRK\n"
- "e.g.:\n"
- "\tset-sign-key -c D8515948451FEA320DC05A2E88308188\n");
-}
-
static bool local_counter(uint32_t *sign_cnt, void *user_data)
{
static uint32_t cnt = 0;
@@ -1226,141 +1051,74 @@ static bool local_counter(uint32_t *sign_cnt, void *user_data)
return true;
}

-static void cmd_set_sign_key(struct client *cli, char *cmd_str)
+static void cmd_set_sign_key(int argc, char **argv)
{
- char *argv[3];
- int argc = 0;
uint8_t key[16];

memset(key, 0, 16);

- if (!parse_args(cmd_str, 2, argv, &argc)) {
- set_sign_key_usage();
- return;
- }
-
- if (argc != 2) {
- set_sign_key_usage();
- return;
- }
-
- if (!strcmp(argv[0], "-c") || !strcmp(argv[0], "--sign-key")) {
- if (convert_sign_key(argv[1], key))
+ if (!strcmp(argv[1], "-c") || !strcmp(argv[1], "--sign-key")) {
+ if (convert_sign_key(argv[2], key))
bt_att_set_local_key(cli->att, key, local_counter, cli);
- } else
- set_sign_key_usage();
-}
-
-static void cmd_help(struct client *cli, char *cmd_str);
-
-typedef void (*command_func_t)(struct client *cli, char *cmd_str);
-
-static struct {
- char *cmd;
- command_func_t func;
- char *doc;
-} command[] = {
- { "help", cmd_help, "\tDisplay help message" },
- { "services", cmd_services, "\tShow discovered services" },
- { "read-value", cmd_read_value,
- "\tRead a characteristic or descriptor value" },
- { "read-long-value", cmd_read_long_value,
- "\tRead a long characteristic or desctriptor value" },
- { "read-multiple", cmd_read_multiple, "\tRead Multiple" },
- { "write-value", cmd_write_value,
- "\tWrite a characteristic or descriptor value" },
- { "write-long-value", cmd_write_long_value,
- "Write long characteristic or descriptor value" },
- { "write-prepare", cmd_write_prepare,
- "\tWrite prepare characteristic or descriptor value" },
- { "write-execute", cmd_write_execute,
- "\tExecute already prepared write" },
- { "register-notify", cmd_register_notify,
- "\tSubscribe to not/ind from a characteristic" },
- { "unregister-notify", cmd_unregister_notify,
- "Unregister a not/ind session"},
- { "set-security", cmd_set_security,
- "\tSet security level on le connection"},
- { "get-security", cmd_get_security,
- "\tGet security level on le connection"},
- { "set-sign-key", cmd_set_sign_key,
- "\tSet signing key for signed write command"},
- { }
-};
-
-static void cmd_help(struct client *cli, char *cmd_str)
-{
- int i;
-
- printf("Commands:\n");
- for (i = 0; command[i].cmd; i++)
- printf("\t%-15s\t%s\n", command[i].cmd, command[i].doc);
-}
-
-static void prompt_read_cb(int fd, uint32_t events, void *user_data)
-{
- ssize_t read;
- size_t len = 0;
- char *line = NULL;
- char *cmd = NULL, *args;
- struct client *cli = user_data;
- int i;
-
- if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
- mainloop_quit();
- return;
- }
-
- read = getline(&line, &len, stdin);
- if (read < 0) {
- free(line);
- return;
- }
-
- if (read <= 1) {
- cmd_help(cli, NULL);
- print_prompt();
- free(line);
- return;
- }
-
- line[read-1] = '\0';
- args = line;
-
- while ((cmd = strsep(&args, " \t")))
- if (*cmd != '\0')
- break;
-
- if (!cmd)
- goto failed;
-
- for (i = 0; command[i].cmd; i++) {
- if (strcmp(command[i].cmd, cmd) == 0)
- break;
+ } else {
+ bt_shell_usage();
+ optind = 0;
}
-
- if (command[i].cmd)
- command[i].func(cli, args);
- else
- fprintf(stderr, "Unknown command: %s\n", line);
-
-failed:
- print_prompt();
-
- free(line);
}

-static void signal_cb(int signum, void *user_data)
-{
- switch (signum) {
- case SIGINT:
- case SIGTERM:
- mainloop_quit();
- break;
- default:
- break;
- }
-}
+static const struct bt_shell_menu main_menu = {
+ .name = "main",
+ .entries = {
+ { "services", "[options...]", cmd_services,
+ "Show discovered services\n"
+ "Options:\n"
+ "\t -u, --uuid <uuid>\tService UUID\n"
+ "\t -a, --handle <handle>\tService start handle\n"
+ "e.g.:\n"
+ "\tservices\n\tservices -u 0x180d\n\tservices -a 0x0009"
+ },
+ { "read-value", "<value_handle>",
+ cmd_read_value, "Read a characteristic or descriptor value" },
+ { "read-long-value", "<value_handle> <offset>",
+ cmd_read_long_value, "Read a long characteristic or desctriptor value" },
+ { "read-multiple", "<handles...>",
+ cmd_read_multiple, "Read Multiple" },
+ { "write-value", " [-w|-s] <value_handle> <value...>",
+ cmd_write_value, "Write a characteristic or descriptor value\n"
+ "Options:\n"
+ "\t-w, --without-response\tWrite without response\n"
+ "\t-s, --signed-write\tSigned write command\n"
+ "e.g.:\n"
+ "\twrite-value 0x0001 00 01 00"
+ },
+ { "write-long-value", "[-r] <value_handle> <offset>",
+ cmd_write_long_value, "Write long characteristic or descriptor value\n"
+ "Options:\n"
+ "\t-r, --reliable-write\tReliable write\n"
+ "e.g.:\n"
+ "\twrite-long-value 0x0001 0 00 01 00"
+ },
+ { "write-prepare", " [options...] <value_handle> <value>",
+ cmd_write_prepare, "Write prepare characteristic or descriptor value\n"
+ "Options:\n"
+ "\t-s, --session-id\tSession id\n"
+ "e.g.:\n"
+ "\twrite-prepare -s 1 0x0001 00 01 00"
+ },
+ { "write-execute", " <session_id> <execute>",
+ cmd_write_execute, "Execute already prepared write" },
+ { "register-notify", "<chrc_value_handle>",
+ cmd_register_notify, "Subscribe to not/ind from a characteristic" },
+ { "unregister-notify", "<notify_id>",
+ cmd_unregister_notify, "Unregister a not/ind session"},
+ { "set-security", "<level 1-3>",
+ cmd_set_security, "Set security level on connection"},
+ { "get-security", NULL,
+ cmd_get_security, "Get security level on connection"},
+ { "set-sign-key", "<csrk>",
+ cmd_set_sign_key, "Set signing key for signed write command"},
+ {} },
+};

static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
int sec)
@@ -1375,15 +1133,15 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
ba2str(src, srcaddr_str);
ba2str(dst, dstaddr_str);

- printf("btgatt-client: Opening L2CAP %s connection on ATT "
- "channel:\n\t src: %s\n\tdest: %s\n",
+ print("btgatt-client: Opening L2CAP %s connection on ATT "
+ "channel:\n\t src: %s\n\tdest: %s",
(dst_type == BDADDR_BREDR ? "BR/EDR" : "LE"),
srcaddr_str, dstaddr_str);
}

sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
if (sock < 0) {
- perror("Failed to create L2CAP socket");
+ error("Failed to create L2CAP socket");
return -1;
}

@@ -1398,7 +1156,7 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
bacpy(&srcaddr.l2_bdaddr, src);

if (bind(sock, (struct sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) {
- perror("Failed to bind L2CAP socket");
+ error("Failed to bind L2CAP socket");
close(sock);
return -1;
}
@@ -1408,7 +1166,7 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
btsec.level = sec;
if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &btsec,
sizeof(btsec)) != 0) {
- fprintf(stderr, "Failed to set L2CAP security level\n");
+ error("Failed to set L2CAP security level");
close(sock);
return -1;
}
@@ -1423,164 +1181,144 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
dstaddr.l2_bdaddr_type = dst_type;
bacpy(&dstaddr.l2_bdaddr, dst);

- printf("Connecting to device...");
+ print("Connecting to device...");
fflush(stdout);

if (connect(sock, (struct sockaddr *) &dstaddr, sizeof(dstaddr)) < 0) {
- perror(" Failed to connect");
+ error("Failed to connect");
close(sock);
return -1;
}

- printf(" Done\n");
+ print("Done");

return sock;
}

-static void usage(void)
-{
- printf("btgatt-client\n");
- printf("Usage:\n\tbtgatt-client [options]\n");
-
- printf("Options:\n"
- "\t-i, --index <id>\t\tSpecify adapter index, e.g. hci0\n"
- "\t-d, --dest <addr>\t\tSpecify the destination address\n"
- "\t-t, --type [random|public|bredr] \tSpecify the address type\n"
- "\t-m, --mtu <mtu> \t\tThe ATT MTU to use\n"
- "\t-s, --security-level <sec> \tSet security level (low|medium|"
- "high|fips)\n"
- "\t-v, --verbose\t\t\tEnable extra logging\n"
- "\t-h, --help\t\t\tDisplay help\n");
-}
-
static struct option main_options[] = {
- { "index", 1, 0, 'i' },
- { "dest", 1, 0, 'd' },
- { "type", 1, 0, 't' },
- { "mtu", 1, 0, 'm' },
- { "security-level", 1, 0, 's' },
- { "verbose", 0, 0, 'v' },
- { "help", 0, 0, 'h' },
+ { "index", required_argument, NULL, 'i' },
+ { "dst-addr", required_argument, NULL, 'd' },
+ { "type", required_argument, NULL, 'T' },
+ { "mtu", required_argument, NULL, 'M' },
+ { "sec-level", required_argument, NULL, 's' },
+ { "verbose", no_argument, NULL, 'V' },
{ }
};

+static const char *index_option;
+static const char *dst_addr_option;
+static const char *type_option;
+static const char *mtu_option;
+static const char *security_level_option;
+static const char *verbose_option;
+
+static const char **optargs[] = {
+ &index_option,
+ &dst_addr_option,
+ &type_option,
+ &mtu_option,
+ &security_level_option,
+ &verbose_option,
+};
+
+static const char *help[] = {
+ "Specify adapter index, e.g. hci0",
+ "Specify the destination address",
+ "Specify the address type (random|public|bredr)",
+ "The ATT MTU to use",
+ "Set security level (low|medium|high|fips)",
+ "Enable extra logging"
+};
+
+static const struct bt_shell_opt opt = {
+ .options = main_options,
+ .optno = sizeof(main_options) / sizeof(struct option),
+ .optstr = "i:d:T:M:s:V",
+ .optarg = optargs,
+ .help = help,
+};
+
int main(int argc, char *argv[])
{
- int opt;
int sec = BT_SECURITY_LOW;
uint16_t mtu = 0;
uint8_t dst_type = BDADDR_LE_PUBLIC;
- bool dst_addr_given = false;
bdaddr_t src_addr, dst_addr;
int dev_id = -1;
int fd;
- struct client *cli;
-
- while ((opt = getopt_long(argc, argv, "+hvs:m:t:d:i:",
- main_options, NULL)) != -1) {
- switch (opt) {
- case 'h':
- usage();
- return EXIT_SUCCESS;
- case 'v':
- verbose = true;
- break;
- case 's':
- if (strcmp(optarg, "low") == 0)
- sec = BT_SECURITY_LOW;
- else if (strcmp(optarg, "medium") == 0)
- sec = BT_SECURITY_MEDIUM;
- else if (strcmp(optarg, "high") == 0)
- sec = BT_SECURITY_HIGH;
- else if (strcmp(optarg, "fips") == 0)
- sec = BT_SECURITY_FIPS;
- else {
- fprintf(stderr, "Invalid security level\n");
- return EXIT_FAILURE;
- }
- break;
- case 'm': {
- int arg;
-
- arg = atoi(optarg);
- if (arg <= 0) {
- fprintf(stderr, "Invalid MTU: %d\n", arg);
- return EXIT_FAILURE;
- }
-
- if (arg > UINT16_MAX) {
- fprintf(stderr, "MTU too large: %d\n", arg);
- return EXIT_FAILURE;
- }
-
- mtu = (uint16_t)arg;
- break;
+ int status;
+
+ bt_shell_init(argc, argv, &opt);
+ bt_shell_set_menu(&main_menu);
+
+ if (verbose_option)
+ verbose = true;
+ if (security_level_option) {
+ if (strcmp(security_level_option, "low") == 0)
+ sec = BT_SECURITY_LOW;
+ else if (strcmp(security_level_option, "medium") == 0)
+ sec = BT_SECURITY_MEDIUM;
+ else if (strcmp(security_level_option, "high") == 0)
+ sec = BT_SECURITY_HIGH;
+ else if (strcmp(security_level_option, "fips") == 0)
+ sec = BT_SECURITY_FIPS;
+ else {
+ error("Invalid security level");
+ return EXIT_FAILURE;
}
- case 't':
- if (strcmp(optarg, "random") == 0)
- dst_type = BDADDR_LE_RANDOM;
- else if (strcmp(optarg, "public") == 0)
- dst_type = BDADDR_LE_PUBLIC;
- else if (strcmp(optarg, "bredr") == 0)
- dst_type = BDADDR_BREDR;
- else {
- fprintf(stderr,
- "Allowed types: random, public, bredr\n");
- return EXIT_FAILURE;
- }
- break;
- case 'd':
- if (str2ba(optarg, &dst_addr) < 0) {
- fprintf(stderr, "Invalid remote address: %s\n",
- optarg);
- return EXIT_FAILURE;
- }
+ }
+ if (mtu_option) {
+ int arg;

- dst_addr_given = true;
- break;
+ arg = atoi(mtu_option);
+ if (arg <= 0) {
+ error("Invalid MTU: %d", arg);
+ return EXIT_FAILURE;
+ }

- case 'i':
- dev_id = hci_devid(optarg);
- if (dev_id < 0) {
- perror("Invalid adapter");
- return EXIT_FAILURE;
- }
+ if (arg > UINT16_MAX) {
+ error("MTU too large: %d", arg);
+ return EXIT_FAILURE;
+ }

- break;
- default:
- fprintf(stderr, "Invalid option: %c\n", opt);
+ mtu = (uint16_t)arg;
+ }
+ if (type_option) {
+ if (strcmp(type_option, "random") == 0)
+ dst_type = BDADDR_LE_RANDOM;
+ else if (strcmp(type_option, "public") == 0)
+ dst_type = BDADDR_LE_PUBLIC;
+ else if (strcmp(type_option, "bredr") == 0)
+ dst_type = BDADDR_BREDR;
+ else {
+ error("Allowed types: random, public, bredr");
return EXIT_FAILURE;
}
}
-
- if (!argc) {
- usage();
- return EXIT_SUCCESS;
+ if (dst_addr_option) {
+ if (str2ba(dst_addr_option, &dst_addr) < 0) {
+ error("Invalid remote address: %s", dst_addr_option);
+ return EXIT_FAILURE;
+ }
+ } else {
+ error("Destination address required!");
+ return EXIT_FAILURE;
}
-
- argc -= optind;
- argv += optind;
- optind = 0;
-
- if (argc) {
- usage();
- return EXIT_SUCCESS;
+ if (index_option) {
+ dev_id = hci_devid(index_option);
+ if (dev_id < 0) {
+ error("Invalid adapter");
+ return EXIT_FAILURE;
+ }
}

if (dev_id == -1)
bacpy(&src_addr, BDADDR_ANY);
else if (hci_devba(dev_id, &src_addr) < 0) {
- perror("Adapter not available");
+ error("Adapter not available");
return EXIT_FAILURE;
}

- if (!dst_addr_given) {
- fprintf(stderr, "Destination address required!\n");
- return EXIT_FAILURE;
- }
-
- mainloop_init();
-
fd = l2cap_att_connect(&src_addr, &dst_addr, dst_type, sec);
if (fd < 0)
return EXIT_FAILURE;
@@ -1591,20 +1329,15 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}

- if (mainloop_add_fd(fileno(stdin),
- EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR,
- prompt_read_cb, cli, NULL) < 0) {
- fprintf(stderr, "Failed to initialize console\n");
- return EXIT_FAILURE;
- }
-
- print_prompt();
-
- mainloop_run_with_signal(signal_cb, NULL);
+ bt_shell_attach(fileno(stdin));
+ update_prompt();
+ shell_running = true;
+ status = bt_shell_run();
+ shell_running = false;

- printf("\n\nShutting down...\n");
+ print("Shutting down...");

client_destroy(cli);

- return EXIT_SUCCESS;
+ return status;
}
--
2.34.1


2023-03-23 21:12:04

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ 4/9] btgatt-client: Rewrite to use bt_shell

Hi Simon,

On Thu, Mar 23, 2023 at 3:44 AM Simon Mikuda
<[email protected]> wrote:

This is a pretty good idea, but make sure you cover each procedure
properly, also be aware that with introduction of the following series
you will probably be able to create scripts:

https://patchwork.kernel.org/project/bluetooth/list/?series=732537

Also in theory much of btgatt-client is now supported in bluetoothctl,
so I wonder why you are not using the daemon to connect, etc?

> ---
> Makefile.tools | 2 +-
> src/shared/shell.h | 1 +
> tools/btgatt-client.c | 1087 ++++++++++++++++-------------------------
> 3 files changed, 412 insertions(+), 678 deletions(-)
>
> diff --git a/Makefile.tools b/Makefile.tools
> index df4cad065..6125e57f7 100644
> --- a/Makefile.tools
> +++ b/Makefile.tools
> @@ -315,7 +315,7 @@ tools_ibeacon_LDADD = src/libshared-mainloop.la
>
> tools_btgatt_client_SOURCES = tools/btgatt-client.c src/uuid-helper.c
> tools_btgatt_client_LDADD = src/libshared-mainloop.la \
> - lib/libbluetooth-internal.la
> + lib/libbluetooth-internal.la -lreadline
>
> tools_btgatt_server_SOURCES = tools/btgatt-server.c src/uuid-helper.c
> tools_btgatt_server_LDADD = src/libshared-mainloop.la \
> diff --git a/src/shared/shell.h b/src/shared/shell.h
> index 87fb5c415..8793835c0 100644
> --- a/src/shared/shell.h
> +++ b/src/shared/shell.h
> @@ -15,6 +15,7 @@
> #define COLOR_GREEN "\001\x1B[0;92m\002"
> #define COLOR_YELLOW "\001\x1B[0;93m\002"
> #define COLOR_BLUE "\001\x1B[0;94m\002"
> +#define COLOR_MAGENTA "\x1B[0;95m"
> #define COLOR_BOLDGRAY "\001\x1B[1;30m\002"
> #define COLOR_BOLDWHITE "\001\x1B[1;37m\002"
> #define COLOR_HIGHLIGHT "\001\x1B[1;39m\002"
> diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
> index 2a0cb5181..ecfe3f3f1 100644
> --- a/tools/btgatt-client.c
> +++ b/tools/btgatt-client.c
> @@ -28,6 +28,7 @@
> #include "lib/uuid.h"
>
> #include "src/shared/mainloop.h"
> +#include "src/shared/shell.h"
> #include "src/shared/util.h"
> #include "src/shared/att.h"
> #include "src/shared/queue.h"
> @@ -37,19 +38,29 @@
> #define ATT_CID 4
> #define ATT_PSM 31
>
> -#define PRLOG(...) \
> - printf(__VA_ARGS__); print_prompt();
> -
> -#define COLOR_OFF "\x1B[0m"
> -#define COLOR_RED "\x1B[0;91m"
> -#define COLOR_GREEN "\x1B[0;92m"
> -#define COLOR_YELLOW "\x1B[0;93m"
> -#define COLOR_BLUE "\x1B[0;94m"
> -#define COLOR_MAGENTA "\x1B[0;95m"
> -#define COLOR_BOLDGRAY "\x1B[1;30m"
> -#define COLOR_BOLDWHITE "\x1B[1;37m"
> +#define MAX_LEN_LINE 512
>
> +struct client *cli;
> static bool verbose = false;
> +static bool shell_running = false;
> +
> +#define print(fmt, arg...) do { \
> + if (shell_running) \
> + bt_shell_printf(fmt "\n", ## arg); \
> + else \
> + printf(fmt "\n", ## arg); \
> +} while (0)
> +
> +#define error(fmt, arg...) do { \
> + if (shell_running) \
> + bt_shell_printf(COLOR_RED fmt "\n" COLOR_OFF, ## arg); \
> + else \
> + fprintf(stderr, COLOR_RED fmt "\n" COLOR_OFF, ## arg); \
> +} while (0)
> +
> +#define append(str, fmt, arg...) do { \
> + sprintf(strchr(str, '\0'), fmt, ## arg); \
> +} while (0)
>
> struct client {
> int fd;
> @@ -60,10 +71,11 @@ struct client {
> unsigned int reliable_session_id;
> };
>
> -static void print_prompt(void)
> +static void update_prompt(void)
> {
> - printf(COLOR_BLUE "[GATT client]" COLOR_OFF "# ");
> - fflush(stdout);
> + char str[32];
> + sprintf(str, COLOR_BLUE "[GATT client]" COLOR_OFF "# ");
> + bt_shell_set_prompt(str);
> }
>
> static const char *ecode_to_string(uint8_t ecode)
> @@ -116,7 +128,7 @@ static const char *ecode_to_string(uint8_t ecode)
>
> static void att_disconnect_cb(int err, void *user_data)
> {
> - printf("Device disconnected: %s\n", strerror(err));
> + print("Device disconnected: %s", strerror(err));
>
> mainloop_quit();
> }
> @@ -125,14 +137,14 @@ static void att_debug_cb(const char *str, void *user_data)
> {
> const char *prefix = user_data;
>
> - PRLOG(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s\n" COLOR_OFF, prefix, str);
> + print(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s" COLOR_OFF, prefix, str);
> }
>
> static void gatt_debug_cb(const char *str, void *user_data)
> {
> const char *prefix = user_data;
>
> - PRLOG(COLOR_GREEN "%s%s\n" COLOR_OFF, prefix, str);
> + print(COLOR_GREEN "%s%s" COLOR_OFF, prefix, str);
> }
>
> static void ready_cb(bool success, uint8_t att_ecode, void *user_data);
> @@ -150,7 +162,7 @@ static void log_service_event(struct gatt_db_attribute *attr, const char *str)
>
> gatt_db_attribute_get_service_handles(attr, &start, &end);
>
> - PRLOG("%s - UUID: %s start: 0x%04x end: 0x%04x\n", str, uuid_str,
> + print("%s - UUID: %s start: 0x%04x end: 0x%04x", str, uuid_str,
> start, end);
> }
>
> @@ -170,20 +182,20 @@ static struct client *client_create(int fd, uint16_t mtu)
>
> cli = new0(struct client, 1);
> if (!cli) {
> - fprintf(stderr, "Failed to allocate memory for client\n");
> + error("Failed to allocate memory for client");
> return NULL;
> }
>
> cli->att = bt_att_new(fd, false);
> if (!cli->att) {
> - fprintf(stderr, "Failed to initialze ATT transport layer\n");
> + error("Failed to initialze ATT transport layer");
> bt_att_unref(cli->att);
> free(cli);
> return NULL;
> }
>
> if (!bt_att_set_close_on_unref(cli->att, true)) {
> - fprintf(stderr, "Failed to set up ATT transport layer\n");
> + error("Failed to set up ATT transport layer");
> bt_att_unref(cli->att);
> free(cli);
> return NULL;
> @@ -191,7 +203,7 @@ static struct client *client_create(int fd, uint16_t mtu)
>
> if (!bt_att_register_disconnect(cli->att, att_disconnect_cb, NULL,
> NULL)) {
> - fprintf(stderr, "Failed to set ATT disconnect handler\n");
> + error("Failed to set ATT disconnect handler");
> bt_att_unref(cli->att);
> free(cli);
> return NULL;
> @@ -200,7 +212,7 @@ static struct client *client_create(int fd, uint16_t mtu)
> cli->fd = fd;
> cli->db = gatt_db_new();
> if (!cli->db) {
> - fprintf(stderr, "Failed to create GATT database\n");
> + error("Failed to create GATT database");
> bt_att_unref(cli->att);
> free(cli);
> return NULL;
> @@ -208,7 +220,7 @@ static struct client *client_create(int fd, uint16_t mtu)
>
> cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu, 0);
> if (!cli->gatt) {
> - fprintf(stderr, "Failed to create GATT client\n");
> + error("Failed to create GATT client");
> gatt_db_unref(cli->db);
> bt_att_unref(cli->att);
> free(cli);
> @@ -225,8 +237,8 @@ static struct client *client_create(int fd, uint16_t mtu)
> NULL);
> }
>
> - bt_gatt_client_ready_register(cli->gatt, ready_cb, cli, NULL);
> - bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, cli,
> + bt_gatt_client_ready_register(cli->gatt, ready_cb, NULL, NULL);
> + bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, NULL,
> NULL);
>
> /* bt_gatt_client already holds a reference */
> @@ -242,7 +254,7 @@ static void client_destroy(struct client *cli)
> free(cli);
> }
>
> -static void print_uuid(const bt_uuid_t *uuid)
> +static void append_uuid(char *str, const bt_uuid_t *uuid)
> {
> char uuid_str[MAX_LEN_UUID_STR];
> bt_uuid_t uuid128;
> @@ -250,15 +262,15 @@ static void print_uuid(const bt_uuid_t *uuid)
> bt_uuid_to_uuid128(uuid, &uuid128);
> bt_uuid_to_string(&uuid128, uuid_str, sizeof(uuid_str));
>
> - printf("%s\n", uuid_str);
> + append(str, "%s", uuid_str);
> }
>
> static void print_incl(struct gatt_db_attribute *attr, void *user_data)
> {
> - struct client *cli = user_data;
> uint16_t handle, start, end;
> struct gatt_db_attribute *service;
> bt_uuid_t uuid;
> + char line[MAX_LEN_LINE] = {0};
>
> if (!gatt_db_attribute_get_incl_data(attr, &handle, &start, &end))
> return;
> @@ -269,18 +281,21 @@ static void print_incl(struct gatt_db_attribute *attr, void *user_data)
>
> gatt_db_attribute_get_service_uuid(service, &uuid);
>
> - printf("\t " COLOR_GREEN "include" COLOR_OFF " - handle: "
> + append(line, "\t " COLOR_GREEN "include" COLOR_OFF " - handle: "
> "0x%04x, - start: 0x%04x, end: 0x%04x,"
> "uuid: ", handle, start, end);
> - print_uuid(&uuid);
> + append_uuid(line, &uuid);
> + print("%s", line);
> }
>
> static void print_desc(struct gatt_db_attribute *attr, void *user_data)
> {
> - printf("\t\t " COLOR_MAGENTA "descr" COLOR_OFF
> + char line[MAX_LEN_LINE] = {0};
> + append(line, "\t\t " COLOR_MAGENTA "descr" COLOR_OFF
> " - handle: 0x%04x, uuid: ",
> gatt_db_attribute_get_handle(attr));
> - print_uuid(gatt_db_attribute_get_type(attr));
> + append_uuid(line, gatt_db_attribute_get_type(attr));
> + print("%s", line);
> }
>
> static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
> @@ -289,6 +304,7 @@ static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
> uint8_t properties;
> uint16_t ext_prop;
> bt_uuid_t uuid;
> + char line[MAX_LEN_LINE] = {0};
>
> if (!gatt_db_attribute_get_char_data(attr, &handle,
> &value_handle,
> @@ -297,170 +313,133 @@ static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
> &uuid))
> return;
>
> - printf("\t " COLOR_YELLOW "charac" COLOR_OFF
> + append(line, "\t " COLOR_YELLOW "charac" COLOR_OFF
> " - start: 0x%04x, value: 0x%04x, "
> "props: 0x%02x, ext_props: 0x%04x, uuid: ",
> handle, value_handle, properties, ext_prop);
> - print_uuid(&uuid);
> + append_uuid(line, &uuid);
> + print("%s", line);
>
> gatt_db_service_foreach_desc(attr, print_desc, NULL);
> }
>
> static void print_service(struct gatt_db_attribute *attr, void *user_data)
> {
> - struct client *cli = user_data;
> uint16_t start, end;
> bool primary;
> bt_uuid_t uuid;
> + char line[MAX_LEN_LINE] = {0};
>
> if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary,
> &uuid))
> return;
>
> - printf(COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
> + append(line, COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
> "end: 0x%04x, type: %s, uuid: ",
> start, end, primary ? "primary" : "secondary");
> - print_uuid(&uuid);
> + append_uuid(line, &uuid);
> + print("%s", line);
>
> - gatt_db_service_foreach_incl(attr, print_incl, cli);
> + gatt_db_service_foreach_incl(attr, print_incl, NULL);
> gatt_db_service_foreach_char(attr, print_chrc, NULL);
> -
> - printf("\n");
> }
>
> static void print_services(struct client *cli)
> {
> - printf("\n");
> -
> - gatt_db_foreach_service(cli->db, NULL, print_service, cli);
> + gatt_db_foreach_service(cli->db, NULL, print_service, NULL);
> }
>
> -static void print_services_by_uuid(struct client *cli, const bt_uuid_t *uuid)
> +static void print_services_by_uuid(const bt_uuid_t *uuid)
> {
> - printf("\n");
> -
> - gatt_db_foreach_service(cli->db, uuid, print_service, cli);
> + gatt_db_foreach_service(cli->db, uuid, print_service, NULL);
> }
>
> -static void print_services_by_handle(struct client *cli, uint16_t handle)
> +static void print_services_by_handle(uint16_t handle)
> {
> - printf("\n");
> -
> /* TODO: Filter by handle */
> gatt_db_foreach_service(cli->db, NULL, print_service, cli);
> }
>
> static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
> {
> - struct client *cli = user_data;
> -
> if (!success) {
> - PRLOG("GATT discovery procedures failed - error code: 0x%02x\n",
> + error("GATT discovery procedures failed - error code: 0x%02x",
> att_ecode);
> return;
> }
>
> - PRLOG("GATT discovery procedures complete\n");
> + print("GATT discovery procedures complete");
>
> print_services(cli);
> - print_prompt();
> }
>
> static void service_changed_cb(uint16_t start_handle, uint16_t end_handle,
> void *user_data)
> {
> - struct client *cli = user_data;
> -
> - printf("\nService Changed handled - start: 0x%04x end: 0x%04x\n",
> + print("Service Changed handled - start: 0x%04x end: 0x%04x",
> start_handle, end_handle);
>
> - gatt_db_foreach_service_in_range(cli->db, NULL, print_service, cli,
> + gatt_db_foreach_service_in_range(cli->db, NULL, print_service, NULL,
> start_handle, end_handle);
> - print_prompt();
> -}
> -
> -static void services_usage(void)
> -{
> - printf("Usage: services [options]\nOptions:\n"
> - "\t -u, --uuid <uuid>\tService UUID\n"
> - "\t -a, --handle <handle>\tService start handle\n"
> - "\t -h, --help\t\tShow help message\n"
> - "e.g.:\n"
> - "\tservices\n\tservices -u 0x180d\n\tservices -a 0x0009\n");
> }
>
> -static bool parse_args(char *str, int expected_argc, char **argv, int *argc)
> -{
> - char **ap;
> -
> - for (ap = argv; (*ap = strsep(&str, " \t")) != NULL;) {
> - if (**ap == '\0')
> - continue;
> -
> - (*argc)++;
> - ap++;
> -
> - if (*argc > expected_argc)
> - return false;
> - }
> -
> - return true;
> -}
> +static struct option services_options[] = {
> + { "uuid", 1, 0, 'u' },
> + { "handle", 1, 0, 'a' },
> + { "help", 0, 0, 'h' },
> + { 0, 0, 0, 0 }
> +};
>
> -static void cmd_services(struct client *cli, char *cmd_str)
> +static void cmd_services(int argc, char **argv)
> {
> - char *argv[3];
> - int argc = 0;
> + int opt;
> + bool use_uuid = false;
> + bt_uuid_t tmp, uuid;
> + uint16_t handle = 0;
> + char *endptr = NULL;
>
> if (!bt_gatt_client_is_ready(cli->gatt)) {
> - printf("GATT client not initialized\n");
> - return;
> - }
> -
> - if (!parse_args(cmd_str, 2, argv, &argc)) {
> - services_usage();
> - return;
> - }
> -
> - if (!argc) {
> - print_services(cli);
> + print("GATT client not initialized");
> return;
> }
>
> - if (argc != 2) {
> - services_usage();
> - return;
> - }
> -
> - if (!strcmp(argv[0], "-u") || !strcmp(argv[0], "--uuid")) {
> - bt_uuid_t tmp, uuid;
> -
> - if (bt_string_to_uuid(&tmp, argv[1]) < 0) {
> - printf("Invalid UUID: %s\n", argv[1]);
> - return;
> - }
> -
> - bt_uuid_to_uuid128(&tmp, &uuid);
> -
> - print_services_by_uuid(cli, &uuid);
> - } else if (!strcmp(argv[0], "-a") || !strcmp(argv[0], "--handle")) {
> - uint16_t handle;
> - char *endptr = NULL;
> -
> - handle = strtol(argv[1], &endptr, 0);
> - if (!endptr || *endptr != '\0') {
> - printf("Invalid start handle: %s\n", argv[1]);
> - return;
> + while ((opt = getopt_long(argc, argv, "u:a:", services_options,
> + NULL)) != -1) {
> + switch (opt) {
> + case 'u':
> + if (bt_string_to_uuid(&tmp, optarg) < 0) {
> + error("Invalid UUID: %s", optarg);
> + optind = 0;
> + return bt_shell_noninteractive_quit(EXIT_FAILURE);
> + }
> + bt_uuid_to_uuid128(&tmp, &uuid);
> + use_uuid = true;
> + break;
> + case 'a':
> + handle = strtol(optarg, &endptr, 0);
> + if (!endptr || *endptr != '\0') {
> + error("Invalid start handle: %s", optarg);
> + optind = 0;
> + return bt_shell_noninteractive_quit(EXIT_FAILURE);
> + }
> + break;
> + case 'h':
> + bt_shell_usage();
> + optind = 0;
> + return bt_shell_noninteractive_quit(EXIT_SUCCESS);
> + default:
> + bt_shell_usage();
> + optind = 0;
> + return bt_shell_noninteractive_quit(EXIT_FAILURE);
> }
> + }
>
> - print_services_by_handle(cli, handle);
> - } else
> - services_usage();
> -}
> + optind = 0;
>
> -static void read_multiple_usage(void)
> -{
> - printf("Usage: read-multiple <handle_1> <handle_2> ...\n");
> + if (use_uuid)
> + print_services_by_uuid(&uuid);
> + else
> + print_services_by_handle(handle);
> }
>
> static void read_multiple_cb(bool success, uint8_t att_ecode,
> @@ -468,43 +447,37 @@ static void read_multiple_cb(bool success, uint8_t att_ecode,
> void *user_data)
> {
> int i;
> + char line[MAX_LEN_LINE] = {0};
>
> if (!success) {
> - PRLOG("\nRead multiple request failed: 0x%02x\n", att_ecode);
> + error("Read multiple request failed: 0x%02x", att_ecode);
> return;
> }
>
> - printf("\nRead multiple value (%u bytes):", length);
> + append(line, "Read multiple value (%u bytes):", length);
>
> for (i = 0; i < length; i++)
> - printf("%02x ", value[i]);
> + append(line, "%02x ", value[i]);
>
> - PRLOG("\n");
> + print("%s", line);
> }
>
> -static void cmd_read_multiple(struct client *cli, char *cmd_str)
> +static void cmd_read_multiple(int argc, char **argv)
> {
> - int argc = 0;
> uint16_t *value;
> - char *argv[512];
> int i;
> char *endptr = NULL;
>
> - if (!parse_args(cmd_str, sizeof(argv), argv, &argc) || argc < 2) {
> - read_multiple_usage();
> - return;
> - }
> -
> value = malloc(sizeof(uint16_t) * argc);
> if (!value) {
> - printf("Failed to construct value\n");
> + error("Failed to construct value");
> return;
> }
>
> - for (i = 0; i < argc; i++) {
> + for (i = 1; i < argc; i++) {
> value[i] = strtol(argv[i], &endptr, 0);
> if (endptr == argv[i] || *endptr != '\0' || !value[i]) {
> - printf("Invalid value byte: %s\n", argv[i]);
> + error("Invalid value byte: %s", argv[i]);
> free(value);
> return;
> }
> @@ -512,133 +485,98 @@ static void cmd_read_multiple(struct client *cli, char *cmd_str)
>
> if (!bt_gatt_client_read_multiple(cli->gatt, value, argc,
> read_multiple_cb, NULL, NULL))
> - printf("Failed to initiate read multiple procedure\n");
> + error("Failed to initiate read multiple procedure");
>
> free(value);
> }
>
> -static void read_value_usage(void)
> -{
> - printf("Usage: read-value <value_handle>\n");
> -}
> -
> static void read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
> uint16_t length, void *user_data)
> {
> int i;
> + char line[MAX_LEN_LINE] = {0};
>
> if (!success) {
> - PRLOG("\nRead request failed: %s (0x%02x)\n",
> + error("Read request failed: %s (0x%02x)",
> ecode_to_string(att_ecode), att_ecode);
> return;
> }
>
> - printf("\nRead value");
> + append(line, "Read value");
>
> if (length == 0) {
> - PRLOG(": 0 bytes\n");
> + print("%s: 0 bytes", line);
> return;
> }
>
> - printf(" (%u bytes): ", length);
> + append(line, " (%u bytes): ", length);
>
> for (i = 0; i < length; i++)
> - printf("%02x ", value[i]);
> + append(line, "%02x ", value[i]);
>
> - PRLOG("\n");
> + print("%s", line);
> }
>
> -static void cmd_read_value(struct client *cli, char *cmd_str)
> +static void cmd_read_value(int argc, char **argv)
> {
> - char *argv[2];
> - int argc = 0;
> uint16_t handle;
> char *endptr = NULL;
>
> - if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) {
> - read_value_usage();
> - return;
> - }
> -
> - handle = strtol(argv[0], &endptr, 0);
> + handle = strtol(argv[1], &endptr, 0);
> if (!endptr || *endptr != '\0' || !handle) {
> - printf("Invalid value handle: %s\n", argv[0]);
> + error("Invalid value handle: %s", argv[1]);
> return;
> }
>
> if (!bt_gatt_client_read_value(cli->gatt, handle, read_cb,
> NULL, NULL))
> - printf("Failed to initiate read value procedure\n");
> -}
> -
> -static void read_long_value_usage(void)
> -{
> - printf("Usage: read-long-value <value_handle> <offset>\n");
> + error("Failed to initiate read value procedure");
> }
>
> -static void cmd_read_long_value(struct client *cli, char *cmd_str)
> +static void cmd_read_long_value(int argc, char **argv)
> {
> - char *argv[3];
> - int argc = 0;
> uint16_t handle;
> uint16_t offset;
> char *endptr = NULL;
>
> - if (!parse_args(cmd_str, 2, argv, &argc) || argc != 2) {
> - read_long_value_usage();
> - return;
> - }
> -
> - handle = strtol(argv[0], &endptr, 0);
> + handle = strtol(argv[1], &endptr, 0);
> if (!endptr || *endptr != '\0' || !handle) {
> - printf("Invalid value handle: %s\n", argv[0]);
> + error("Invalid value handle: %s", argv[1]);
> return;
> }
>
> endptr = NULL;
> - offset = strtol(argv[1], &endptr, 0);
> + offset = strtol(argv[2], &endptr, 0);
> if (!endptr || *endptr != '\0') {
> - printf("Invalid offset: %s\n", argv[1]);
> + error("Invalid offset: %s", argv[2]);
> return;
> }
>
> if (!bt_gatt_client_read_long_value(cli->gatt, handle, offset, read_cb,
> NULL, NULL))
> - printf("Failed to initiate read long value procedure\n");
> -}
> -
> -static void write_value_usage(void)
> -{
> - printf("Usage: write-value [options] <value_handle> <value>\n"
> - "Options:\n"
> - "\t-w, --without-response\tWrite without response\n"
> - "\t-s, --signed-write\tSigned write command\n"
> - "e.g.:\n"
> - "\twrite-value 0x0001 00 01 00\n");
> + error("Failed to initiate read long value procedure");
> }
>
> static struct option write_value_options[] = {
> { "without-response", 0, 0, 'w' },
> { "signed-write", 0, 0, 's' },
> + { "help", 0, 0, 'h' },
> { }
> };
>
> static void write_cb(bool success, uint8_t att_ecode, void *user_data)
> {
> if (success) {
> - PRLOG("\nWrite successful\n");
> + print("Write successful");
> } else {
> - PRLOG("\nWrite failed: %s (0x%02x)\n",
> + error("Write failed: %s (0x%02x)",
> ecode_to_string(att_ecode), att_ecode);
> }
> }
>
> -static void cmd_write_value(struct client *cli, char *cmd_str)
> +static void cmd_write_value(int argc, char **argv)
> {
> int opt, i, val;
> - char *argvbuf[516];
> - char **argv = argvbuf;
> - int argc = 1;
> uint16_t handle;
> char *endptr = NULL;
> int length;
> @@ -646,14 +584,6 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
> bool without_response = false;
> bool signed_write = false;
>
> - if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
> - printf("Too many arguments\n");
> - write_value_usage();
> - return;
> - }
> -
> - optind = 0;
> - argv[0] = "write-value";
> while ((opt = getopt_long(argc, argv, "+ws", write_value_options,
> NULL)) != -1) {
> switch (opt) {
> @@ -663,23 +593,24 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
> case 's':
> signed_write = true;
> break;
> + case 'h':
> + bt_shell_usage();
> + optind = 0;
> + return bt_shell_noninteractive_quit(EXIT_SUCCESS);
> default:
> - write_value_usage();
> - return;
> + bt_shell_usage();
> + optind = 0;
> + return bt_shell_noninteractive_quit(EXIT_FAILURE);
> }
> }
>
> argc -= optind;
> argv += optind;
> -
> - if (argc < 1) {
> - write_value_usage();
> - return;
> - }
> + optind = 0;
>
> handle = strtol(argv[0], &endptr, 0);
> if (!endptr || *endptr != '\0' || !handle) {
> - printf("Invalid handle: %s\n", argv[0]);
> + error("Invalid handle: %s", argv[1]);
> return;
> }
>
> @@ -687,13 +618,13 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
>
> if (length > 0) {
> if (length > UINT16_MAX) {
> - printf("Write value too long\n");
> + error("Write value too long");
> return;
> }
>
> value = malloc(length);
> if (!value) {
> - printf("Failed to construct write value\n");
> + error("Failed to construct write value");
> return;
> }
>
> @@ -701,7 +632,7 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
> val = strtol(argv[i], &endptr, 0);
> if (endptr == argv[i] || *endptr != '\0'
> || errno == ERANGE || val < 0 || val > 255) {
> - printf("Invalid value byte: %s\n",
> + error("Invalid value byte: %s",
> argv[i]);
> goto done;
> }
> @@ -712,36 +643,27 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
> if (without_response) {
> if (!bt_gatt_client_write_without_response(cli->gatt, handle,
> signed_write, value, length)) {
> - printf("Failed to initiate write without response "
> - "procedure\n");
> + error("Failed to initiate write without response "
> + "procedure");
> goto done;
> }
>
> - printf("Write command sent\n");
> + print("Write command sent");
> goto done;
> }
>
> if (!bt_gatt_client_write_value(cli->gatt, handle, value, length,
> write_cb,
> NULL, NULL))
> - printf("Failed to initiate write procedure\n");
> + error("Failed to initiate write procedure");
>
> done:
> free(value);
> }
>
> -static void write_long_value_usage(void)
> -{
> - printf("Usage: write-long-value [options] <value_handle> <offset> "
> - "<value>\n"
> - "Options:\n"
> - "\t-r, --reliable-write\tReliable write\n"
> - "e.g.:\n"
> - "\twrite-long-value 0x0001 0 00 01 00\n");
> -}
> -
> static struct option write_long_value_options[] = {
> { "reliable-write", 0, 0, 'r' },
> + { "help", 0, 0, 'h' },
> { }
> };
>
> @@ -749,21 +671,18 @@ static void write_long_cb(bool success, bool reliable_error, uint8_t att_ecode,
> void *user_data)
> {
> if (success) {
> - PRLOG("Write successful\n");
> + print("Write successful");
> } else if (reliable_error) {
> - PRLOG("Reliable write not verified\n");
> + error("Reliable write not verified");
> } else {
> - PRLOG("\nWrite failed: %s (0x%02x)\n",
> + error("Write failed: %s (0x%02x)",
> ecode_to_string(att_ecode), att_ecode);
> }
> }
>
> -static void cmd_write_long_value(struct client *cli, char *cmd_str)
> +static void cmd_write_long_value(int argc, char **argv)
> {
> int opt, i, val;
> - char *argvbuf[516];
> - char **argv = argvbuf;
> - int argc = 1;
> uint16_t handle;
> uint16_t offset;
> char *endptr = NULL;
> @@ -771,44 +690,44 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)
> uint8_t *value = NULL;
> bool reliable_writes = false;
>
> - if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
> - printf("Too many arguments\n");
> - write_value_usage();
> - return;
> - }
> -
> - optind = 0;
> - argv[0] = "write-long-value";
> while ((opt = getopt_long(argc, argv, "+r", write_long_value_options,
> NULL)) != -1) {
> switch (opt) {
> case 'r':
> reliable_writes = true;
> break;
> + case 'h':
> + bt_shell_usage();
> + optind = 0;
> + return bt_shell_noninteractive_quit(EXIT_SUCCESS);
> default:
> - write_long_value_usage();
> - return;
> + bt_shell_usage();
> + optind = 0;
> + return bt_shell_noninteractive_quit(EXIT_FAILURE);
> }
> }
>
> argc -= optind;
> argv += optind;
> + optind = 0;
>
> - if (argc < 2) {
> - write_long_value_usage();
> + if (argc > 514) {
> + error("Too many arguments");
> + bt_shell_usage();
> + optind = 0;
> return;
> }
>
> handle = strtol(argv[0], &endptr, 0);
> if (!endptr || *endptr != '\0' || !handle) {
> - printf("Invalid handle: %s\n", argv[0]);
> + error("Invalid handle: %s", argv[1]);
> return;
> }
>
> endptr = NULL;
> offset = strtol(argv[1], &endptr, 0);
> if (!endptr || *endptr != '\0' || errno == ERANGE) {
> - printf("Invalid offset: %s\n", argv[1]);
> + error("Invalid offset: %s", argv[2]);
> return;
> }
>
> @@ -816,13 +735,13 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)
>
> if (length > 0) {
> if (length > UINT16_MAX) {
> - printf("Write value too long\n");
> + error("Write value too long");
> return;
> }
>
> value = malloc(length);
> if (!value) {
> - printf("Failed to construct write value\n");
> + error("Failed to construct write value");
> return;
> }
>
> @@ -830,7 +749,7 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)
> val = strtol(argv[i], &endptr, 0);
> if (endptr == argv[i] || *endptr != '\0'
> || errno == ERANGE || val < 0 || val > 255) {
> - printf("Invalid value byte: %s\n",
> + error("Invalid value byte: %s",
> argv[i]);
> free(value);
> return;
> @@ -843,32 +762,20 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)
> offset, value, length,
> write_long_cb,
> NULL, NULL))
> - printf("Failed to initiate long write procedure\n");
> + error("Failed to initiate long write procedure");
>
> free(value);
> }
>
> -static void write_prepare_usage(void)
> -{
> - printf("Usage: write-prepare [options] <value_handle> <offset> "
> - "<value>\n"
> - "Options:\n"
> - "\t-s, --session-id\tSession id\n"
> - "e.g.:\n"
> - "\twrite-prepare -s 1 0x0001 00 01 00\n");
> -}
> -
> static struct option write_prepare_options[] = {
> { "session-id", 1, 0, 's' },
> + { "help", 0, 0, 'h' },
> { }
> };
>
> -static void cmd_write_prepare(struct client *cli, char *cmd_str)
> +static void cmd_write_prepare(int argc, char **argv)
> {
> int opt, i, val;
> - char *argvbuf[516];
> - char **argv = argvbuf;
> - int argc = 0;
> unsigned int id = 0;
> uint16_t handle;
> uint16_t offset;
> @@ -876,59 +783,50 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str)
> unsigned int length;
> uint8_t *value = NULL;
>
> - if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
> - printf("Too many arguments\n");
> - write_value_usage();
> - return;
> - }
> -
> - /* Add command name for getopt_long */
> - argc++;
> - argv[0] = "write-prepare";
> -
> - optind = 0;
> while ((opt = getopt_long(argc, argv , "s:", write_prepare_options,
> NULL)) != -1) {
> switch (opt) {
> case 's':
> - if (!optarg) {
> - write_prepare_usage();
> - return;
> - }
> -
> id = atoi(optarg);
> -
> break;
> + case 'h':
> + bt_shell_usage();
> + optind = 0;
> + return bt_shell_noninteractive_quit(EXIT_SUCCESS);
> default:
> - write_prepare_usage();
> - return;
> + bt_shell_usage();
> + optind = 0;
> + return bt_shell_noninteractive_quit(EXIT_FAILURE);
> }
> }
>
> argc -= optind;
> argv += optind;
> + optind = 0;
>
> - if (argc < 3) {
> - write_prepare_usage();
> + if (argc > 514) {
> + error("Too many arguments");
> + bt_shell_usage();
> + optind = 0;
> return;
> }
>
> if (cli->reliable_session_id != id) {
> - printf("Session id != Ongoing session id (%u!=%u)\n", id,
> + error("Session id != Ongoing session id (%u!=%u)", id,
> cli->reliable_session_id);
> return;
> }
>
> handle = strtol(argv[0], &endptr, 0);
> if (!endptr || *endptr != '\0' || !handle) {
> - printf("Invalid handle: %s\n", argv[0]);
> + error("Invalid handle: %s", argv[1]);
> return;
> }
>
> endptr = NULL;
> offset = strtol(argv[1], &endptr, 0);
> if (!endptr || *endptr != '\0' || errno == ERANGE) {
> - printf("Invalid offset: %s\n", argv[1]);
> + error("Invalid offset: %s", argv[2]);
> return;
> }
>
> @@ -942,13 +840,13 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str)
> goto done;
>
> if (length > UINT16_MAX) {
> - printf("Write value too long\n");
> + error("Write value too long");
> return;
> }
>
> value = malloc(length);
> if (!value) {
> - printf("Failed to allocate memory for value\n");
> + error("Failed to allocate memory for value");
> return;
> }
>
> @@ -956,7 +854,7 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str)
> val = strtol(argv[i], &endptr, 0);
> if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE
> || val < 0 || val > 255) {
> - printf("Invalid value byte: %s\n", argv[i]);
> + error("Invalid value byte: %s", argv[i]);
> free(value);
> return;
> }
> @@ -971,64 +869,43 @@ done:
> write_long_cb, NULL,
> NULL);
> if (!cli->reliable_session_id)
> - printf("Failed to proceed prepare write\n");
> + error("Failed to proceed prepare write");
> else
> - printf("Prepare write success.\n"
> - "Session id: %d to be used on next write\n",
> + print("Prepare write success."
> + "Session id: %d to be used on next write",
> cli->reliable_session_id);
>
> free(value);
> }
>
> -static void write_execute_usage(void)
> -{
> - printf("Usage: write-execute <session_id> <execute>\n"
> - "e.g.:\n"
> - "\twrite-execute 1 0\n");
> -}
> -
> -static void cmd_write_execute(struct client *cli, char *cmd_str)
> +static void cmd_write_execute(int argc, char **argv)
> {
> - char *argvbuf[516];
> - char **argv = argvbuf;
> - int argc = 0;
> char *endptr = NULL;
> unsigned int session_id;
> bool execute;
>
> - if (!parse_args(cmd_str, 514, argv, &argc)) {
> - printf("Too many arguments\n");
> - write_value_usage();
> - return;
> - }
> -
> - if (argc < 2) {
> - write_execute_usage();
> - return;
> - }
> -
> - session_id = strtol(argv[0], &endptr, 0);
> + session_id = strtol(argv[1], &endptr, 0);
> if (!endptr || *endptr != '\0') {
> - printf("Invalid session id: %s\n", argv[0]);
> + error("Invalid session id: %s", argv[1]);
> return;
> }
>
> if (session_id != cli->reliable_session_id) {
> - printf("Invalid session id: %u != %u\n", session_id,
> + error("Invalid session id: %u != %u", session_id,
> cli->reliable_session_id);
> return;
> }
>
> - execute = !!strtol(argv[1], &endptr, 0);
> + execute = !!strtol(argv[2], &endptr, 0);
> if (!endptr || *endptr != '\0') {
> - printf("Invalid execute: %s\n", argv[1]);
> + error("Invalid execute: %s", argv[2]);
> return;
> }
>
> if (execute) {
> if (!bt_gatt_client_write_execute(cli->gatt, session_id,
> write_cb, NULL, NULL))
> - printf("Failed to proceed write execute\n");
> + error("Failed to proceed write execute");
> } else {
> bt_gatt_client_cancel(cli->gatt, session_id);
> }
> @@ -1036,46 +913,40 @@ static void cmd_write_execute(struct client *cli, char *cmd_str)
> cli->reliable_session_id = 0;
> }
>
> -static void register_notify_usage(void)
> -{
> - printf("Usage: register-notify <chrc value handle>\n");
> -}
> -
> static void notify_cb(uint16_t value_handle, const uint8_t *value,
> uint16_t length, void *user_data)
> {
> int i;
> + char line[MAX_LEN_LINE] = {0};
>
> - printf("\n\tHandle Value Not/Ind: 0x%04x - ", value_handle);
> + append(line, "\tHandle Value Not/Ind: 0x%04x - ", value_handle);
>
> if (length == 0) {
> - PRLOG("(0 bytes)\n");
> + print("%s(0 bytes)", line);
> return;
> }
>
> - printf("(%u bytes): ", length);
> + append(line, "(%u bytes): ", length);
>
> for (i = 0; i < length; i++)
> - printf("%02x ", value[i]);
> + append(line, "%02x ", value[i]);
>
> - PRLOG("\n");
> + print("%s", line);
> }
>
> static void register_notify_cb(uint16_t att_ecode, void *user_data)
> {
> if (att_ecode) {
> - PRLOG("Failed to register notify handler "
> - "- error code: 0x%02x\n", att_ecode);
> + error("Failed to register notify handler "
> + "- error code: 0x%02x", att_ecode);
> return;
> }
>
> - PRLOG("Registered notify handler!\n");
> + print("Registered notify handler!");
> }
>
> -static void cmd_register_notify(struct client *cli, char *cmd_str)
> +static void cmd_register_notify(int argc, char **argv)
> {
> - char *argv[2];
> - int argc = 0;
> uint16_t value_handle;
> unsigned int id;
> char *endptr = NULL;
> @@ -1085,14 +956,9 @@ static void cmd_register_notify(struct client *cli, char *cmd_str)
> return;
> }
>
> - if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) {
> - register_notify_usage();
> - return;
> - }
> -
> - value_handle = strtol(argv[0], &endptr, 0);
> + value_handle = strtol(argv[1], &endptr, 0);
> if (!endptr || *endptr != '\0' || !value_handle) {
> - printf("Invalid value handle: %s\n", argv[0]);
> + error("Invalid value handle: %s", argv[1]);
> return;
> }
>
> @@ -1100,22 +966,15 @@ static void cmd_register_notify(struct client *cli, char *cmd_str)
> register_notify_cb,
> notify_cb, NULL, NULL);
> if (!id) {
> - printf("Failed to register notify handler\n");
> + error("Failed to register notify handler");
> return;
> }
>
> - printf("Registering notify handler with id: %u\n", id);
> + print("Registering notify handler with id: %u", id);
> }
>
> -static void unregister_notify_usage(void)
> +static void cmd_unregister_notify(int argc, char **argv)
> {
> - printf("Usage: unregister-notify <notify id>\n");
> -}
> -
> -static void cmd_unregister_notify(struct client *cli, char *cmd_str)
> -{
> - char *argv[2];
> - int argc = 0;
> unsigned int id;
> char *endptr = NULL;
>
> @@ -1124,72 +983,46 @@ static void cmd_unregister_notify(struct client *cli, char *cmd_str)
> return;
> }
>
> - if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) {
> - unregister_notify_usage();
> - return;
> - }
> -
> - id = strtol(argv[0], &endptr, 0);
> + id = strtol(argv[1], &endptr, 0);
> if (!endptr || *endptr != '\0' || !id) {
> - printf("Invalid notify id: %s\n", argv[0]);
> + error("Invalid notify id: %s", argv[1]);
> return;
> }
>
> if (!bt_gatt_client_unregister_notify(cli->gatt, id)) {
> - printf("Failed to unregister notify handler with id: %u\n", id);
> + error("Failed to unregister notify handler with id: %u", id);
> return;
> }
>
> - printf("Unregistered notify handler with id: %u\n", id);
> -}
> -
> -static void set_security_usage(void)
> -{
> - printf("Usage: set-security <level>\n"
> - "level: 1-3\n"
> - "e.g.:\n"
> - "\tset-security 2\n");
> + print("Unregistered notify handler with id: %u", id);
> }
>
> -static void cmd_set_security(struct client *cli, char *cmd_str)
> +static void cmd_set_security(int argc, char **argv)
> {
> - char *argv[2];
> - int argc = 0;
> char *endptr = NULL;
> int level;
>
> - if (!parse_args(cmd_str, 1, argv, &argc)) {
> - printf("Too many arguments\n");
> - set_security_usage();
> - return;
> - }
> -
> - if (argc < 1) {
> - set_security_usage();
> - return;
> - }
> -
> - level = strtol(argv[0], &endptr, 0);
> + level = strtol(argv[1], &endptr, 0);
> if (!endptr || *endptr != '\0' || level < 1 || level > 3) {
> - printf("Invalid level: %s\n", argv[0]);
> + error("Invalid level: %s", argv[1]);
> return;
> }
>
> if (!bt_gatt_client_set_security(cli->gatt, level))
> - printf("Could not set sec level\n");
> + error("Could not set sec level");
> else
> - printf("Setting security level %d success\n", level);
> + print("Setting security level %d success", level);
> }
>
> -static void cmd_get_security(struct client *cli, char *cmd_str)
> +static void cmd_get_security(int argc, char **argv)
> {
> int level;
>
> level = bt_gatt_client_get_security(cli->gatt);
> if (level < 0)
> - printf("Could not set sec level\n");
> + error("Could not get sec level");
> else
> - printf("Security level: %u\n", level);
> + print("Security level: %u", level);
> }
>
> static bool convert_sign_key(char *optarg, uint8_t key[16])
> @@ -1197,7 +1030,7 @@ static bool convert_sign_key(char *optarg, uint8_t key[16])
> int i;
>
> if (strlen(optarg) != 32) {
> - printf("sign-key length is invalid\n");
> + error("sign-key length is invalid");
> return false;
> }
>
> @@ -1209,14 +1042,6 @@ static bool convert_sign_key(char *optarg, uint8_t key[16])
> return true;
> }
>
> -static void set_sign_key_usage(void)
> -{
> - printf("Usage: set-sign-key [options]\nOptions:\n"
> - "\t -c, --sign-key <csrk>\tCSRK\n"
> - "e.g.:\n"
> - "\tset-sign-key -c D8515948451FEA320DC05A2E88308188\n");
> -}
> -
> static bool local_counter(uint32_t *sign_cnt, void *user_data)
> {
> static uint32_t cnt = 0;
> @@ -1226,141 +1051,74 @@ static bool local_counter(uint32_t *sign_cnt, void *user_data)
> return true;
> }
>
> -static void cmd_set_sign_key(struct client *cli, char *cmd_str)
> +static void cmd_set_sign_key(int argc, char **argv)
> {
> - char *argv[3];
> - int argc = 0;
> uint8_t key[16];
>
> memset(key, 0, 16);
>
> - if (!parse_args(cmd_str, 2, argv, &argc)) {
> - set_sign_key_usage();
> - return;
> - }
> -
> - if (argc != 2) {
> - set_sign_key_usage();
> - return;
> - }
> -
> - if (!strcmp(argv[0], "-c") || !strcmp(argv[0], "--sign-key")) {
> - if (convert_sign_key(argv[1], key))
> + if (!strcmp(argv[1], "-c") || !strcmp(argv[1], "--sign-key")) {
> + if (convert_sign_key(argv[2], key))
> bt_att_set_local_key(cli->att, key, local_counter, cli);
> - } else
> - set_sign_key_usage();
> -}
> -
> -static void cmd_help(struct client *cli, char *cmd_str);
> -
> -typedef void (*command_func_t)(struct client *cli, char *cmd_str);
> -
> -static struct {
> - char *cmd;
> - command_func_t func;
> - char *doc;
> -} command[] = {
> - { "help", cmd_help, "\tDisplay help message" },
> - { "services", cmd_services, "\tShow discovered services" },
> - { "read-value", cmd_read_value,
> - "\tRead a characteristic or descriptor value" },
> - { "read-long-value", cmd_read_long_value,
> - "\tRead a long characteristic or desctriptor value" },
> - { "read-multiple", cmd_read_multiple, "\tRead Multiple" },
> - { "write-value", cmd_write_value,
> - "\tWrite a characteristic or descriptor value" },
> - { "write-long-value", cmd_write_long_value,
> - "Write long characteristic or descriptor value" },
> - { "write-prepare", cmd_write_prepare,
> - "\tWrite prepare characteristic or descriptor value" },
> - { "write-execute", cmd_write_execute,
> - "\tExecute already prepared write" },
> - { "register-notify", cmd_register_notify,
> - "\tSubscribe to not/ind from a characteristic" },
> - { "unregister-notify", cmd_unregister_notify,
> - "Unregister a not/ind session"},
> - { "set-security", cmd_set_security,
> - "\tSet security level on le connection"},
> - { "get-security", cmd_get_security,
> - "\tGet security level on le connection"},
> - { "set-sign-key", cmd_set_sign_key,
> - "\tSet signing key for signed write command"},
> - { }
> -};
> -
> -static void cmd_help(struct client *cli, char *cmd_str)
> -{
> - int i;
> -
> - printf("Commands:\n");
> - for (i = 0; command[i].cmd; i++)
> - printf("\t%-15s\t%s\n", command[i].cmd, command[i].doc);
> -}
> -
> -static void prompt_read_cb(int fd, uint32_t events, void *user_data)
> -{
> - ssize_t read;
> - size_t len = 0;
> - char *line = NULL;
> - char *cmd = NULL, *args;
> - struct client *cli = user_data;
> - int i;
> -
> - if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
> - mainloop_quit();
> - return;
> - }
> -
> - read = getline(&line, &len, stdin);
> - if (read < 0) {
> - free(line);
> - return;
> - }
> -
> - if (read <= 1) {
> - cmd_help(cli, NULL);
> - print_prompt();
> - free(line);
> - return;
> - }
> -
> - line[read-1] = '\0';
> - args = line;
> -
> - while ((cmd = strsep(&args, " \t")))
> - if (*cmd != '\0')
> - break;
> -
> - if (!cmd)
> - goto failed;
> -
> - for (i = 0; command[i].cmd; i++) {
> - if (strcmp(command[i].cmd, cmd) == 0)
> - break;
> + } else {
> + bt_shell_usage();
> + optind = 0;
> }
> -
> - if (command[i].cmd)
> - command[i].func(cli, args);
> - else
> - fprintf(stderr, "Unknown command: %s\n", line);
> -
> -failed:
> - print_prompt();
> -
> - free(line);
> }
>
> -static void signal_cb(int signum, void *user_data)
> -{
> - switch (signum) {
> - case SIGINT:
> - case SIGTERM:
> - mainloop_quit();
> - break;
> - default:
> - break;
> - }
> -}
> +static const struct bt_shell_menu main_menu = {
> + .name = "main",
> + .entries = {
> + { "services", "[options...]", cmd_services,
> + "Show discovered services\n"
> + "Options:\n"
> + "\t -u, --uuid <uuid>\tService UUID\n"
> + "\t -a, --handle <handle>\tService start handle\n"
> + "e.g.:\n"
> + "\tservices\n\tservices -u 0x180d\n\tservices -a 0x0009"
> + },
> + { "read-value", "<value_handle>",
> + cmd_read_value, "Read a characteristic or descriptor value" },
> + { "read-long-value", "<value_handle> <offset>",
> + cmd_read_long_value, "Read a long characteristic or desctriptor value" },
> + { "read-multiple", "<handles...>",
> + cmd_read_multiple, "Read Multiple" },
> + { "write-value", " [-w|-s] <value_handle> <value...>",
> + cmd_write_value, "Write a characteristic or descriptor value\n"
> + "Options:\n"
> + "\t-w, --without-response\tWrite without response\n"
> + "\t-s, --signed-write\tSigned write command\n"
> + "e.g.:\n"
> + "\twrite-value 0x0001 00 01 00"
> + },
> + { "write-long-value", "[-r] <value_handle> <offset>",
> + cmd_write_long_value, "Write long characteristic or descriptor value\n"
> + "Options:\n"
> + "\t-r, --reliable-write\tReliable write\n"
> + "e.g.:\n"
> + "\twrite-long-value 0x0001 0 00 01 00"
> + },
> + { "write-prepare", " [options...] <value_handle> <value>",
> + cmd_write_prepare, "Write prepare characteristic or descriptor value\n"
> + "Options:\n"
> + "\t-s, --session-id\tSession id\n"
> + "e.g.:\n"
> + "\twrite-prepare -s 1 0x0001 00 01 00"
> + },
> + { "write-execute", " <session_id> <execute>",
> + cmd_write_execute, "Execute already prepared write" },
> + { "register-notify", "<chrc_value_handle>",
> + cmd_register_notify, "Subscribe to not/ind from a characteristic" },
> + { "unregister-notify", "<notify_id>",
> + cmd_unregister_notify, "Unregister a not/ind session"},
> + { "set-security", "<level 1-3>",
> + cmd_set_security, "Set security level on connection"},
> + { "get-security", NULL,
> + cmd_get_security, "Get security level on connection"},
> + { "set-sign-key", "<csrk>",
> + cmd_set_sign_key, "Set signing key for signed write command"},
> + {} },
> +};
>
> static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
> int sec)
> @@ -1375,15 +1133,15 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
> ba2str(src, srcaddr_str);
> ba2str(dst, dstaddr_str);
>
> - printf("btgatt-client: Opening L2CAP %s connection on ATT "
> - "channel:\n\t src: %s\n\tdest: %s\n",
> + print("btgatt-client: Opening L2CAP %s connection on ATT "
> + "channel:\n\t src: %s\n\tdest: %s",
> (dst_type == BDADDR_BREDR ? "BR/EDR" : "LE"),
> srcaddr_str, dstaddr_str);
> }
>
> sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
> if (sock < 0) {
> - perror("Failed to create L2CAP socket");
> + error("Failed to create L2CAP socket");
> return -1;
> }
>
> @@ -1398,7 +1156,7 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
> bacpy(&srcaddr.l2_bdaddr, src);
>
> if (bind(sock, (struct sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) {
> - perror("Failed to bind L2CAP socket");
> + error("Failed to bind L2CAP socket");
> close(sock);
> return -1;
> }
> @@ -1408,7 +1166,7 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
> btsec.level = sec;
> if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &btsec,
> sizeof(btsec)) != 0) {
> - fprintf(stderr, "Failed to set L2CAP security level\n");
> + error("Failed to set L2CAP security level");
> close(sock);
> return -1;
> }
> @@ -1423,164 +1181,144 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
> dstaddr.l2_bdaddr_type = dst_type;
> bacpy(&dstaddr.l2_bdaddr, dst);
>
> - printf("Connecting to device...");
> + print("Connecting to device...");
> fflush(stdout);
>
> if (connect(sock, (struct sockaddr *) &dstaddr, sizeof(dstaddr)) < 0) {
> - perror(" Failed to connect");
> + error("Failed to connect");
> close(sock);
> return -1;
> }
>
> - printf(" Done\n");
> + print("Done");
>
> return sock;
> }
>
> -static void usage(void)
> -{
> - printf("btgatt-client\n");
> - printf("Usage:\n\tbtgatt-client [options]\n");
> -
> - printf("Options:\n"
> - "\t-i, --index <id>\t\tSpecify adapter index, e.g. hci0\n"
> - "\t-d, --dest <addr>\t\tSpecify the destination address\n"
> - "\t-t, --type [random|public|bredr] \tSpecify the address type\n"
> - "\t-m, --mtu <mtu> \t\tThe ATT MTU to use\n"
> - "\t-s, --security-level <sec> \tSet security level (low|medium|"
> - "high|fips)\n"
> - "\t-v, --verbose\t\t\tEnable extra logging\n"
> - "\t-h, --help\t\t\tDisplay help\n");
> -}
> -
> static struct option main_options[] = {
> - { "index", 1, 0, 'i' },
> - { "dest", 1, 0, 'd' },
> - { "type", 1, 0, 't' },
> - { "mtu", 1, 0, 'm' },
> - { "security-level", 1, 0, 's' },
> - { "verbose", 0, 0, 'v' },
> - { "help", 0, 0, 'h' },
> + { "index", required_argument, NULL, 'i' },
> + { "dst-addr", required_argument, NULL, 'd' },
> + { "type", required_argument, NULL, 'T' },
> + { "mtu", required_argument, NULL, 'M' },
> + { "sec-level", required_argument, NULL, 's' },
> + { "verbose", no_argument, NULL, 'V' },
> { }
> };
>
> +static const char *index_option;
> +static const char *dst_addr_option;
> +static const char *type_option;
> +static const char *mtu_option;
> +static const char *security_level_option;
> +static const char *verbose_option;
> +
> +static const char **optargs[] = {
> + &index_option,
> + &dst_addr_option,
> + &type_option,
> + &mtu_option,
> + &security_level_option,
> + &verbose_option,
> +};
> +
> +static const char *help[] = {
> + "Specify adapter index, e.g. hci0",
> + "Specify the destination address",
> + "Specify the address type (random|public|bredr)",
> + "The ATT MTU to use",
> + "Set security level (low|medium|high|fips)",
> + "Enable extra logging"
> +};
> +
> +static const struct bt_shell_opt opt = {
> + .options = main_options,
> + .optno = sizeof(main_options) / sizeof(struct option),
> + .optstr = "i:d:T:M:s:V",
> + .optarg = optargs,
> + .help = help,
> +};
> +
> int main(int argc, char *argv[])
> {
> - int opt;
> int sec = BT_SECURITY_LOW;
> uint16_t mtu = 0;
> uint8_t dst_type = BDADDR_LE_PUBLIC;
> - bool dst_addr_given = false;
> bdaddr_t src_addr, dst_addr;
> int dev_id = -1;
> int fd;
> - struct client *cli;
> -
> - while ((opt = getopt_long(argc, argv, "+hvs:m:t:d:i:",
> - main_options, NULL)) != -1) {
> - switch (opt) {
> - case 'h':
> - usage();
> - return EXIT_SUCCESS;
> - case 'v':
> - verbose = true;
> - break;
> - case 's':
> - if (strcmp(optarg, "low") == 0)
> - sec = BT_SECURITY_LOW;
> - else if (strcmp(optarg, "medium") == 0)
> - sec = BT_SECURITY_MEDIUM;
> - else if (strcmp(optarg, "high") == 0)
> - sec = BT_SECURITY_HIGH;
> - else if (strcmp(optarg, "fips") == 0)
> - sec = BT_SECURITY_FIPS;
> - else {
> - fprintf(stderr, "Invalid security level\n");
> - return EXIT_FAILURE;
> - }
> - break;
> - case 'm': {
> - int arg;
> -
> - arg = atoi(optarg);
> - if (arg <= 0) {
> - fprintf(stderr, "Invalid MTU: %d\n", arg);
> - return EXIT_FAILURE;
> - }
> -
> - if (arg > UINT16_MAX) {
> - fprintf(stderr, "MTU too large: %d\n", arg);
> - return EXIT_FAILURE;
> - }
> -
> - mtu = (uint16_t)arg;
> - break;
> + int status;
> +
> + bt_shell_init(argc, argv, &opt);
> + bt_shell_set_menu(&main_menu);
> +
> + if (verbose_option)
> + verbose = true;
> + if (security_level_option) {
> + if (strcmp(security_level_option, "low") == 0)
> + sec = BT_SECURITY_LOW;
> + else if (strcmp(security_level_option, "medium") == 0)
> + sec = BT_SECURITY_MEDIUM;
> + else if (strcmp(security_level_option, "high") == 0)
> + sec = BT_SECURITY_HIGH;
> + else if (strcmp(security_level_option, "fips") == 0)
> + sec = BT_SECURITY_FIPS;
> + else {
> + error("Invalid security level");
> + return EXIT_FAILURE;
> }
> - case 't':
> - if (strcmp(optarg, "random") == 0)
> - dst_type = BDADDR_LE_RANDOM;
> - else if (strcmp(optarg, "public") == 0)
> - dst_type = BDADDR_LE_PUBLIC;
> - else if (strcmp(optarg, "bredr") == 0)
> - dst_type = BDADDR_BREDR;
> - else {
> - fprintf(stderr,
> - "Allowed types: random, public, bredr\n");
> - return EXIT_FAILURE;
> - }
> - break;
> - case 'd':
> - if (str2ba(optarg, &dst_addr) < 0) {
> - fprintf(stderr, "Invalid remote address: %s\n",
> - optarg);
> - return EXIT_FAILURE;
> - }
> + }
> + if (mtu_option) {
> + int arg;
>
> - dst_addr_given = true;
> - break;
> + arg = atoi(mtu_option);
> + if (arg <= 0) {
> + error("Invalid MTU: %d", arg);
> + return EXIT_FAILURE;
> + }
>
> - case 'i':
> - dev_id = hci_devid(optarg);
> - if (dev_id < 0) {
> - perror("Invalid adapter");
> - return EXIT_FAILURE;
> - }
> + if (arg > UINT16_MAX) {
> + error("MTU too large: %d", arg);
> + return EXIT_FAILURE;
> + }
>
> - break;
> - default:
> - fprintf(stderr, "Invalid option: %c\n", opt);
> + mtu = (uint16_t)arg;
> + }
> + if (type_option) {
> + if (strcmp(type_option, "random") == 0)
> + dst_type = BDADDR_LE_RANDOM;
> + else if (strcmp(type_option, "public") == 0)
> + dst_type = BDADDR_LE_PUBLIC;
> + else if (strcmp(type_option, "bredr") == 0)
> + dst_type = BDADDR_BREDR;
> + else {
> + error("Allowed types: random, public, bredr");
> return EXIT_FAILURE;
> }
> }
> -
> - if (!argc) {
> - usage();
> - return EXIT_SUCCESS;
> + if (dst_addr_option) {
> + if (str2ba(dst_addr_option, &dst_addr) < 0) {
> + error("Invalid remote address: %s", dst_addr_option);
> + return EXIT_FAILURE;
> + }
> + } else {
> + error("Destination address required!");
> + return EXIT_FAILURE;
> }
> -
> - argc -= optind;
> - argv += optind;
> - optind = 0;
> -
> - if (argc) {
> - usage();
> - return EXIT_SUCCESS;
> + if (index_option) {
> + dev_id = hci_devid(index_option);
> + if (dev_id < 0) {
> + error("Invalid adapter");
> + return EXIT_FAILURE;
> + }
> }
>
> if (dev_id == -1)
> bacpy(&src_addr, BDADDR_ANY);
> else if (hci_devba(dev_id, &src_addr) < 0) {
> - perror("Adapter not available");
> + error("Adapter not available");
> return EXIT_FAILURE;
> }
>
> - if (!dst_addr_given) {
> - fprintf(stderr, "Destination address required!\n");
> - return EXIT_FAILURE;
> - }
> -
> - mainloop_init();
> -
> fd = l2cap_att_connect(&src_addr, &dst_addr, dst_type, sec);
> if (fd < 0)
> return EXIT_FAILURE;
> @@ -1591,20 +1329,15 @@ int main(int argc, char *argv[])
> return EXIT_FAILURE;
> }
>
> - if (mainloop_add_fd(fileno(stdin),
> - EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR,
> - prompt_read_cb, cli, NULL) < 0) {
> - fprintf(stderr, "Failed to initialize console\n");
> - return EXIT_FAILURE;
> - }
> -
> - print_prompt();
> -
> - mainloop_run_with_signal(signal_cb, NULL);
> + bt_shell_attach(fileno(stdin));
> + update_prompt();
> + shell_running = true;
> + status = bt_shell_run();
> + shell_running = false;
>
> - printf("\n\nShutting down...\n");
> + print("Shutting down...");
>
> client_destroy(cli);
>
> - return EXIT_SUCCESS;
> + return status;
> }
> --
> 2.34.1
>


--
Luiz Augusto von Dentz