2011-02-08 15:32:12

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH 1/5] Put all gatttool global options in a unique struct

---
attrib/gatttool.c | 186 +++++++++++++++++++++++++++++++----------------------
1 files changed, 108 insertions(+), 78 deletions(-)

diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index 8e8ed8e..c612309 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -46,32 +46,56 @@
/* Minimum MTU for L2CAP connections over BR/EDR */
#define ATT_MIN_MTU_L2CAP 48

-static gchar *opt_src = NULL;
-static gchar *opt_dst = NULL;
-static gchar *opt_value = NULL;
-static gchar *opt_sec_level = "low";
-static uuid_t *opt_uuid = NULL;
-static int opt_start = 0x0001;
-static int opt_end = 0xffff;
-static int opt_handle = -1;
-static int opt_mtu = 0;
-static int opt_psm = 0x1f;
-static gboolean opt_primary = FALSE;
-static gboolean opt_characteristics = FALSE;
-static gboolean opt_char_read = FALSE;
-static gboolean opt_listen = FALSE;
-static gboolean opt_char_desc = FALSE;
-static gboolean opt_le = FALSE;
-static gboolean opt_char_write = FALSE;
static GMainLoop *event_loop;
static gboolean got_error = FALSE;

+static struct main_opts {
+ gchar *src;
+ gchar *dst;
+ gchar *value;
+ gchar *sec_level;
+ uuid_t *uuid;
+ int start;
+ int end;
+ int handle;
+ int mtu;
+ int psm;
+ gboolean primary;
+ gboolean char_disc;
+ gboolean char_read;
+ gboolean listen;
+ gboolean char_desc;
+ gboolean le;
+ gboolean char_write;
+} main_opts;
+
struct characteristic_data {
GAttrib *attrib;
uint16_t start;
uint16_t end;
};

+static void options_init(void)
+{
+ main_opts.src = NULL;
+ main_opts.dst = NULL;
+ main_opts.value = NULL;
+ main_opts.sec_level = "low";
+ main_opts.uuid = NULL;
+ main_opts.start = 0x0001;
+ main_opts.end = 0xffff;
+ main_opts.handle = -1;
+ main_opts.mtu = 0;
+ main_opts.psm = 0x1f;
+ main_opts.primary = FALSE;
+ main_opts.char_disc = FALSE;
+ main_opts.char_read = FALSE;
+ main_opts.listen = FALSE;
+ main_opts.char_desc = FALSE;
+ main_opts.le = FALSE;
+ main_opts.char_write = FALSE;
+}
+
static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
{
if (err) {
@@ -90,31 +114,31 @@ static GIOChannel *do_connect(gboolean le)

/* This check is required because currently setsockopt() returns no
* errors for MTU values smaller than the allowed minimum. */
- if (opt_mtu != 0 && opt_mtu < ATT_MIN_MTU_L2CAP) {
+ if (main_opts.mtu != 0 && main_opts.mtu < ATT_MIN_MTU_L2CAP) {
g_printerr("MTU cannot be smaller than %d\n",
ATT_MIN_MTU_L2CAP);
return NULL;
}

/* Remote device */
- if (opt_dst == NULL) {
+ if (main_opts.dst == NULL) {
g_printerr("Remote Bluetooth address required\n");
return NULL;
}
- str2ba(opt_dst, &dba);
+ str2ba(main_opts.dst, &dba);

/* Local adapter */
- if (opt_src != NULL) {
- if (!strncmp(opt_src, "hci", 3))
- hci_devba(atoi(opt_src + 3), &sba);
+ if (main_opts.src != NULL) {
+ if (!strncmp(main_opts.src, "hci", 3))
+ hci_devba(atoi(main_opts.src + 3), &sba);
else
- str2ba(opt_src, &sba);
+ str2ba(main_opts.src, &sba);
} else
bacpy(&sba, BDADDR_ANY);

- if (strcmp(opt_sec_level, "medium") == 0)
+ if (strcmp(main_opts.sec_level, "medium") == 0)
sec_level = BT_IO_SEC_MEDIUM;
- else if (strcmp(opt_sec_level, "high") == 0)
+ else if (strcmp(main_opts.sec_level, "high") == 0)
sec_level = BT_IO_SEC_HIGH;
else
sec_level = BT_IO_SEC_LOW;
@@ -124,15 +148,15 @@ static GIOChannel *do_connect(gboolean le)
BT_IO_OPT_SOURCE_BDADDR, &sba,
BT_IO_OPT_DEST_BDADDR, &dba,
BT_IO_OPT_CID, GATT_CID,
- BT_IO_OPT_OMTU, opt_mtu,
+ BT_IO_OPT_OMTU, main_opts.mtu,
BT_IO_OPT_SEC_LEVEL, sec_level,
BT_IO_OPT_INVALID);
else
chan = bt_io_connect(BT_IO_L2CAP, connect_cb, NULL, NULL, &err,
BT_IO_OPT_SOURCE_BDADDR, &sba,
BT_IO_OPT_DEST_BDADDR, &dba,
- BT_IO_OPT_PSM, opt_psm,
- BT_IO_OPT_OMTU, opt_mtu,
+ BT_IO_OPT_PSM, main_opts.psm,
+ BT_IO_OPT_OMTU, main_opts.mtu,
BT_IO_OPT_SEC_LEVEL, sec_level,
BT_IO_OPT_INVALID);

@@ -236,9 +260,9 @@ static gboolean primary(gpointer user_data)
{
GAttrib *attrib = user_data;

- if (opt_uuid)
- gatt_discover_primary(attrib, opt_uuid, primary_by_uuid_cb,
- NULL);
+ if (main_opts.uuid)
+ gatt_discover_primary(attrib, main_opts.uuid,
+ primary_by_uuid_cb, NULL);
else
gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);

@@ -272,7 +296,8 @@ static gboolean characteristics(gpointer user_data)
{
GAttrib *attrib = user_data;

- gatt_discover_char(attrib, opt_start, opt_end, char_discovered_cb, NULL);
+ gatt_discover_char(attrib, main_opts.start, main_opts.end,
+ char_discovered_cb, NULL);

return FALSE;
}
@@ -298,7 +323,7 @@ static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
g_print("\n");

done:
- if (opt_listen == FALSE)
+ if (main_opts.listen == FALSE)
g_main_loop_quit(event_loop);
}

@@ -310,7 +335,7 @@ static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
int i;

if (status == ATT_ECODE_ATTR_NOT_FOUND &&
- char_data->start != opt_start)
+ char_data->start != main_opts.start)
goto done;

if (status != 0) {
@@ -339,7 +364,7 @@ static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
att_data_list_free(list);

gatt_read_char_by_uuid(char_data->attrib, char_data->start,
- char_data->end, opt_uuid,
+ char_data->end, main_opts.uuid,
char_read_by_uuid_cb,
char_data);

@@ -353,27 +378,28 @@ static gboolean characteristics_read(gpointer user_data)
{
GAttrib *attrib = user_data;

- if (opt_uuid != NULL) {
+ if (main_opts.uuid != NULL) {
struct characteristic_data *char_data;

char_data = g_new(struct characteristic_data, 1);
char_data->attrib = attrib;
- char_data->start = opt_start;
- char_data->end = opt_end;
+ char_data->start = main_opts.start;
+ char_data->end = main_opts.end;

- gatt_read_char_by_uuid(attrib, opt_start, opt_end, opt_uuid,
- char_read_by_uuid_cb, char_data);
+ gatt_read_char_by_uuid(attrib, main_opts.start, main_opts.end,
+ main_opts.uuid, char_read_by_uuid_cb,
+ char_data);

return FALSE;
}

- if (opt_handle <= 0) {
+ if (main_opts.handle <= 0) {
g_printerr("A valid handle is required\n");
g_main_loop_quit(event_loop);
return FALSE;
}

- gatt_read_char(attrib, opt_handle, char_read_cb, attrib);
+ gatt_read_char(attrib, main_opts.handle, char_read_cb, attrib);

return FALSE;
}
@@ -411,23 +437,24 @@ static gboolean characteristics_write(gpointer user_data)
uint8_t *value;
size_t len;

- if (opt_handle <= 0) {
+ if (main_opts.handle <= 0) {
g_printerr("A valid handle is required\n");
goto error;
}

- if (opt_value == NULL || opt_value[0] == '\0') {
+ if (main_opts.value == NULL || main_opts.value[0] == '\0') {
g_printerr("A value is required\n");
goto error;
}

- len = attr_data_from_string(opt_value, &value);
+ len = attr_data_from_string(main_opts.value, &value);
if (len == 0) {
g_printerr("Invalid value\n");
goto error;
}

- gatt_write_cmd(attrib, opt_handle, value, len, mainloop_quit, value);
+ gatt_write_cmd(attrib, main_opts.handle, value, len, mainloop_quit,
+ value);

return FALSE;

@@ -474,7 +501,7 @@ static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen,
att_data_list_free(list);

done:
- if (opt_listen == FALSE)
+ if (main_opts.listen == FALSE)
g_main_loop_quit(event_loop);
}

@@ -482,7 +509,8 @@ static gboolean characteristics_desc(gpointer user_data)
{
GAttrib *attrib = user_data;

- gatt_find_info(attrib, opt_start, opt_end, char_desc_cb, NULL);
+ gatt_find_info(attrib, main_opts.start, main_opts.end,
+ char_desc_cb, NULL);

return FALSE;
}
@@ -493,20 +521,20 @@ static gboolean parse_uuid(const char *key, const char *value,
if (!value)
return FALSE;

- opt_uuid = g_try_malloc(sizeof(uuid_t));
- if (opt_uuid == NULL)
+ main_opts.uuid = g_try_malloc(sizeof(uuid_t));
+ if (main_opts.uuid == NULL)
return FALSE;

- if (bt_string2uuid(opt_uuid, value) < 0)
+ if (bt_string2uuid(main_opts.uuid, value) < 0)
return FALSE;

return TRUE;
}

static GOptionEntry primary_char_options[] = {
- { "start", 's' , 0, G_OPTION_ARG_INT, &opt_start,
+ { "start", 's' , 0, G_OPTION_ARG_INT, &main_opts.start,
"Starting handle(optional)", "0x0001" },
- { "end", 'e' , 0, G_OPTION_ARG_INT, &opt_end,
+ { "end", 'e' , 0, G_OPTION_ARG_INT, &main_opts.end,
"Ending handle(optional)", "0xffff" },
{ "uuid", 'u', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
parse_uuid, "UUID16 or UUID128(optional)", "0x1801"},
@@ -514,42 +542,42 @@ static GOptionEntry primary_char_options[] = {
};

static GOptionEntry char_rw_options[] = {
- { "handle", 'a' , 0, G_OPTION_ARG_INT, &opt_handle,
+ { "handle", 'a' , 0, G_OPTION_ARG_INT, &main_opts.handle,
"Read/Write characteristic by handle(required)", "0x0001" },
- { "value", 'n' , 0, G_OPTION_ARG_STRING, &opt_value,
+ { "value", 'n' , 0, G_OPTION_ARG_STRING, &main_opts.value,
"Write characteristic value (required for write operation)",
"0x0001" },
{NULL},
};

static GOptionEntry gatt_options[] = {
- { "primary", 0, 0, G_OPTION_ARG_NONE, &opt_primary,
+ { "primary", 0, 0, G_OPTION_ARG_NONE, &main_opts.primary,
"Primary Service Discovery", NULL },
- { "characteristics", 0, 0, G_OPTION_ARG_NONE, &opt_characteristics,
+ { "characteristics", 0, 0, G_OPTION_ARG_NONE, &main_opts.char_disc,
"Characteristics Discovery", NULL },
- { "char-read", 0, 0, G_OPTION_ARG_NONE, &opt_char_read,
+ { "char-read", 0, 0, G_OPTION_ARG_NONE, &main_opts.char_read,
"Characteristics Value/Descriptor Read", NULL },
- { "char-write", 0, 0, G_OPTION_ARG_NONE, &opt_char_write,
+ { "char-write", 0, 0, G_OPTION_ARG_NONE, &main_opts.char_write,
"Characteristics Value Write", NULL },
- { "char-desc", 0, 0, G_OPTION_ARG_NONE, &opt_char_desc,
+ { "char-desc", 0, 0, G_OPTION_ARG_NONE, &main_opts.char_desc,
"Characteristics Descriptor Discovery", NULL },
- { "listen", 0, 0, G_OPTION_ARG_NONE, &opt_listen,
+ { "listen", 0, 0, G_OPTION_ARG_NONE, &main_opts.listen,
"Listen for notifications and indications", NULL },
- { "le", 0, 0, G_OPTION_ARG_NONE, &opt_le,
+ { "le", 0, 0, G_OPTION_ARG_NONE, &main_opts.le,
"Use Bluetooth Low Energy transport", NULL },
{ NULL },
};

static GOptionEntry options[] = {
- { "adapter", 'i', 0, G_OPTION_ARG_STRING, &opt_src,
+ { "adapter", 'i', 0, G_OPTION_ARG_STRING, &main_opts.src,
"Specify local adapter interface", "hciX" },
- { "device", 'b', 0, G_OPTION_ARG_STRING, &opt_dst,
+ { "device", 'b', 0, G_OPTION_ARG_STRING, &main_opts.dst,
"Specify remote Bluetooth address", "MAC" },
- { "mtu", 'm', 0, G_OPTION_ARG_INT, &opt_mtu,
+ { "mtu", 'm', 0, G_OPTION_ARG_INT, &main_opts.mtu,
"Specify the MTU size", "MTU" },
- { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
+ { "psm", 'p', 0, G_OPTION_ARG_INT, &main_opts.psm,
"Specify the PSM for GATT/ATT over BR/EDR", "PSM" },
- { "sec-level", 'l', 0, G_OPTION_ARG_STRING, &opt_sec_level,
+ { "sec-level", 'l', 0, G_OPTION_ARG_STRING, &main_opts.sec_level,
"Set security level. Default: low", "[low | medium | high]"},
{ NULL },
};
@@ -563,6 +591,8 @@ int main(int argc, char *argv[])
GIOChannel *chan;
GSourceFunc callback;

+ options_init();
+
context = g_option_context_new(NULL);
g_option_context_add_main_entries(context, options, NULL);

@@ -594,15 +624,15 @@ int main(int argc, char *argv[])
g_error_free(gerr);
}

- if (opt_primary)
+ if (main_opts.primary)
callback = primary;
- else if (opt_characteristics)
+ else if (main_opts.char_disc)
callback = characteristics;
- else if (opt_char_read)
+ else if (main_opts.char_read)
callback = characteristics_read;
- else if (opt_char_write)
+ else if (main_opts.char_write)
callback = characteristics_write;
- else if (opt_char_desc)
+ else if (main_opts.char_desc)
callback = characteristics_desc;
else {
gchar *help = g_option_context_get_help(context, TRUE, NULL);
@@ -612,7 +642,7 @@ int main(int argc, char *argv[])
goto done;
}

- chan = do_connect(opt_le);
+ chan = do_connect(main_opts.le);
if (chan == NULL) {
got_error = TRUE;
goto done;
@@ -622,7 +652,7 @@ int main(int argc, char *argv[])

event_loop = g_main_loop_new(NULL, FALSE);

- if (opt_listen)
+ if (main_opts.listen)
g_idle_add(listen_start, attrib);

g_idle_add(callback, attrib);
@@ -638,9 +668,9 @@ int main(int argc, char *argv[])

done:
g_option_context_free(context);
- g_free(opt_src);
- g_free(opt_dst);
- g_free(opt_uuid);
+ g_free(main_opts.src);
+ g_free(main_opts.dst);
+ g_free(main_opts.uuid);

if (got_error)
exit(EXIT_FAILURE);
--
1.7.1



2011-02-15 17:20:05

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH v3 1/3] Include check to readline lib on acinlude.m4

Hi Sheldon,

On Mon, Feb 14, 2011, Sheldon Demario wrote:
> ---
> acinclude.m4 | 10 ++++++++++
> 1 files changed, 10 insertions(+), 0 deletions(-)

All three patches (v4 of 2/3) have been pushed upstream. Thanks.

Johan

2011-02-15 17:09:43

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH v4] Add an initial interactive mode to gatttool

Mode required to allow better GATT procedures control. Some scenarios
require sequential commands without disconnection and delay between
operations. It is also desirable to change some connection parameters
of an active connection.
---
Makefile.am | 8 ++-
attrib/gatttool.c | 9 ++++
attrib/gatttool.h | 24 +++++++++
attrib/interactive.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++
configure.ac | 1 +
5 files changed, 168 insertions(+), 2 deletions(-)
create mode 100644 attrib/gatttool.h
create mode 100644 attrib/interactive.c

diff --git a/Makefile.am b/Makefile.am
index 275f511..7e5d0ee 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -175,12 +175,16 @@ builtin_sources += plugins/service.c
endif

if ATTRIBPLUGIN
+
+if READLINE
bin_PROGRAMS += attrib/gatttool

attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
attrib/gattrib.c btio/btio.c \
- src/glib-helper.h src/glib-helper.c
-attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@
+ src/glib-helper.h src/glib-helper.c \
+ attrib/gatttool.h attrib/interactive.c
+attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @READLINE_LIBS@
+endif

builtin_modules += attrib
builtin_sources += attrib/main.c \
diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index b9f0087..c3d0472 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -42,6 +42,7 @@
#include "gattrib.h"
#include "glib-helper.h"
#include "gatt.h"
+#include "gatttool.h"

/* Minimum MTU for L2CAP connections over BR/EDR */
#define ATT_MIN_MTU_L2CAP 48
@@ -64,6 +65,7 @@ static gboolean opt_char_desc = FALSE;
static gboolean opt_le = FALSE;
static gboolean opt_char_write = FALSE;
static gboolean opt_char_write_req = FALSE;
+static gboolean opt_interactive = FALSE;
static GMainLoop *event_loop;
static gboolean got_error = FALSE;

@@ -594,6 +596,8 @@ static GOptionEntry gatt_options[] = {
"Listen for notifications and indications", NULL },
{ "le", 0, 0, G_OPTION_ARG_NONE, &opt_le,
"Use Bluetooth Low Energy transport", NULL },
+ { "interactive", 'I', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE,
+ &opt_interactive, "Use interactive mode", NULL },
{ NULL },
};

@@ -651,6 +655,11 @@ int main(int argc, char *argv[])
g_error_free(gerr);
}

+ if (opt_interactive) {
+ interactive();
+ goto done;
+ }
+
if (opt_primary)
callback = primary;
else if (opt_characteristics)
diff --git a/attrib/gatttool.h b/attrib/gatttool.h
new file mode 100644
index 0000000..ed5d9d6
--- /dev/null
+++ b/attrib/gatttool.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int interactive(void);
diff --git a/attrib/interactive.c b/attrib/interactive.c
new file mode 100644
index 0000000..0653609
--- /dev/null
+++ b/attrib/interactive.c
@@ -0,0 +1,128 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "gatttool.h"
+
+static GMainLoop *event_loop;
+
+static void cmd_help(int argcp, char **argvp);
+
+static void cmd_exit(int argcp, char **argvp)
+{
+ rl_callback_handler_remove();
+ g_main_loop_quit(event_loop);
+}
+
+static struct {
+ const char *cmd;
+ void (*func)(int argcp, char **argvp);
+ const char *desc;
+} commands[] = {
+ { "help", cmd_help, "Show this help"},
+ { "exit", cmd_exit, "Exit interactive mode"},
+ { NULL, NULL, NULL}
+};
+
+static void cmd_help(int argcp, char **argvp)
+{
+ int i;
+
+ for (i = 0; commands[i].cmd; i++)
+ printf("%-12s\t%s\n", commands[i].cmd, commands[i].desc);
+}
+
+static void parse_line(char *line_read)
+{
+ gchar **argvp;
+ int argcp;
+ int i;
+
+ if (line_read == NULL) {
+ printf("\n");
+ cmd_exit(0, NULL);
+ return;
+ }
+
+ line_read = g_strstrip(line_read);
+
+ if (*line_read == '\0')
+ return;
+
+ add_history(line_read);
+
+ g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
+
+ for (i = 0; commands[i].cmd; i++)
+ if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
+ break;
+
+ if (commands[i].cmd)
+ commands[i].func(argcp, argvp);
+ else
+ printf("%s: command not found\n", argvp[0]);
+
+ g_strfreev(argvp);
+}
+
+static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ g_io_channel_unref(chan);
+ return FALSE;
+ }
+
+ rl_callback_read_char();
+
+ return TRUE;
+}
+
+int interactive(void)
+{
+ GIOChannel *pchan;
+ gint events;
+
+ event_loop = g_main_loop_new(NULL, FALSE);
+
+ pchan = g_io_channel_unix_new(fileno(stdin));
+ g_io_channel_set_close_on_unref(pchan, TRUE);
+ events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ g_io_add_watch(pchan, events, prompt_read, NULL);
+
+ rl_callback_handler_install("> ", parse_line);
+
+ g_main_loop_run(event_loop);
+
+ rl_callback_handler_remove();
+ g_io_channel_unref(pchan);
+ g_main_loop_unref(event_loop);
+
+ return 0;
+}
diff --git a/configure.ac b/configure.ac
index 88dd177..0002af2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,7 @@ AC_PATH_GSTREAMER
AC_PATH_USB
AC_PATH_SNDFILE
AC_PATH_OUI
+AC_PATH_READLINE

AC_ARG_BLUEZ

--
1.7.1


2011-02-14 18:40:08

by Sheldon Demario

[permalink] [raw]
Subject: Re: [PATCH v3 2/3] Add an initial interactive mode to gatttool

Hi Gustavo,

On Mon, Feb 14, 2011 at 3:13 PM, Gustavo F. Padovan
<[email protected]> wrote:
> Hi Sheldon,
>
> * Sheldon Demario <[email protected]> [2011-02-14 12:07:19 -0300]:
>
>> Mode required to allow better GATT procedures control. Some scenarios
>> require sequential commands without disconnection and delay between
>> operations. It is also desirable to change some connection parameters
>> of an active connection.
>> ---
>> ?Makefile.am ? ? ? ? ?| ? ?8 ++-
>> ?attrib/gatttool.c ? ?| ? ?9 ++++
>> ?attrib/gatttool.h ? ?| ? 24 +++++++++
>> ?attrib/interactive.c | ?128 ++++++++++++++++++++++++++++++++++++++++++++++++++
>> ?configure.ac ? ? ? ? | ? ?1 +
>> ?5 files changed, 168 insertions(+), 2 deletions(-)
>> ?create mode 100644 attrib/gatttool.h
>> ?create mode 100644 attrib/interactive.c
>>
>> diff --git a/Makefile.am b/Makefile.am
>> index e6639a7..11f990b 100644
>> --- a/Makefile.am
>> +++ b/Makefile.am
>> @@ -175,12 +175,16 @@ builtin_sources += plugins/service.c
>> ?endif
>>
>> ?if ATTRIBPLUGIN
>> +
>> +if READLINE
>
> Is that really needed? You already checked for READLINE in ./configure at
> this point.

Actually ./configure just check if it exists and doesn't fail if so.
The libreadline doesn't have pkgconfig files to make this process
easy, thats why it is just not compiled if it is missing.

>
>> ?bin_PROGRAMS += attrib/gatttool
>>
>> ?attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
>> ? ? ? ? ? ? ? ? ? ? ? ? attrib/gattrib.c btio/btio.c \
>> - ? ? ? ? ? ? ? ? ? ? ? src/glib-helper.h src/glib-helper.c
>> -attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@
>> + ? ? ? ? ? ? ? ? ? ? ? src/glib-helper.h src/glib-helper.c \
>> + ? ? ? ? ? ? ? ? ? ? ? attrib/gatttool.h attrib/interactive.c
>> +attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @READLINE_LIBS@
>> +endif
>>
>> ?builtin_modules += attrib
>> ?builtin_sources += attrib/main.c \
>> diff --git a/attrib/gatttool.c b/attrib/gatttool.c
>> index 8e8ed8e..d663eb5 100644
>> --- a/attrib/gatttool.c
>> +++ b/attrib/gatttool.c
>> @@ -42,6 +42,7 @@
>> ?#include "gattrib.h"
>> ?#include "glib-helper.h"
>> ?#include "gatt.h"
>> +#include "gatttool.h"
>>
>> ?/* Minimum MTU for L2CAP connections over BR/EDR */
>> ?#define ATT_MIN_MTU_L2CAP 48
>> @@ -63,6 +64,7 @@ static gboolean opt_listen = FALSE;
>> ?static gboolean opt_char_desc = FALSE;
>> ?static gboolean opt_le = FALSE;
>> ?static gboolean opt_char_write = FALSE;
>> +static gboolean opt_interactive = FALSE;
>> ?static GMainLoop *event_loop;
>> ?static gboolean got_error = FALSE;
>>
>> @@ -537,6 +539,8 @@ static GOptionEntry gatt_options[] = {
>> ? ? ? ? ? ? ? "Listen for notifications and indications", NULL },
>> ? ? ? { "le", 0, 0, G_OPTION_ARG_NONE, &opt_le,
>> ? ? ? ? ? ? ? "Use Bluetooth Low Energy transport", NULL },
>> + ? ? { "interactive", 'I', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE,
>> + ? ? ? ? ? ? &opt_interactive, "Use interactive mode", NULL },
>> ? ? ? { NULL },
>> ?};
>>
>> @@ -594,6 +598,11 @@ int main(int argc, char *argv[])
>> ? ? ? ? ? ? ? g_error_free(gerr);
>> ? ? ? }
>>
>> + ? ? if (opt_interactive) {
>> + ? ? ? ? ? ? interactive();
>> + ? ? ? ? ? ? goto done;
>> + ? ? }
>> +
>> ? ? ? if (opt_primary)
>> ? ? ? ? ? ? ? callback = primary;
>> ? ? ? else if (opt_characteristics)
>> diff --git a/attrib/gatttool.h b/attrib/gatttool.h
>> new file mode 100644
>> index 0000000..ed5d9d6
>> --- /dev/null
>> +++ b/attrib/gatttool.h
>> @@ -0,0 +1,24 @@
>> +/*
>> + *
>> + * ?BlueZ - Bluetooth protocol stack for Linux
>> + *
>> + * ?Copyright (C) 2011 ?Nokia Corporation
>> + *
>> + *
>> + * ?This program is free software; you can redistribute it and/or modify
>> + * ?it under the terms of the GNU General Public License as published by
>> + * ?the Free Software Foundation; either version 2 of the License, or
>> + * ?(at your option) any later version.
>> + *
>> + * ?This program is distributed in the hope that it will be useful,
>> + * ?but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * ?MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
>> + * ?GNU General Public License for more details.
>> + *
>> + * ?You should have received a copy of the GNU General Public License
>> + * ?along with this program; if not, write to the Free Software
>> + * ?Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ?02110-1301 ?USA
>> + *
>> + */
>> +
>> +int interactive(void);
>> diff --git a/attrib/interactive.c b/attrib/interactive.c
>> new file mode 100644
>> index 0000000..0653609
>> --- /dev/null
>> +++ b/attrib/interactive.c
>> @@ -0,0 +1,128 @@
>> +/*
>> + *
>> + * ?BlueZ - Bluetooth protocol stack for Linux
>> + *
>> + * ?Copyright (C) 2011 ?Nokia Corporation
>> + *
>> + *
>> + * ?This program is free software; you can redistribute it and/or modify
>> + * ?it under the terms of the GNU General Public License as published by
>> + * ?the Free Software Foundation; either version 2 of the License, or
>> + * ?(at your option) any later version.
>> + *
>> + * ?This program is distributed in the hope that it will be useful,
>> + * ?but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * ?MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
>> + * ?GNU General Public License for more details.
>> + *
>> + * ?You should have received a copy of the GNU General Public License
>> + * ?along with this program; if not, write to the Free Software
>> + * ?Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ?02110-1301 ?USA
>> + *
>> + */
>> +#include <strings.h>
>> +#include <stdlib.h>
>> +#include <stdio.h>
>> +#include <glib.h>
>> +
>> +#include <readline/readline.h>
>> +#include <readline/history.h>
>> +
>> +#include "gatttool.h"
>> +
>> +static GMainLoop *event_loop;
>> +
>> +static void cmd_help(int argcp, char **argvp);
>
> Just move cmd_help() up to avoid this.
>
>> +
>> +static void cmd_exit(int argcp, char **argvp)
>> +{
>> + ? ? rl_callback_handler_remove();
>> + ? ? g_main_loop_quit(event_loop);
>> +}
>> +
>> +static struct {
>> + ? ? const char *cmd;
>> + ? ? void (*func)(int argcp, char **argvp);
>> + ? ? const char *desc;
>> +} commands[] = {
>> + ? ? { "help", ? ? ? cmd_help, ? ? ? "Show this help"},
>> + ? ? { "exit", ? ? ? cmd_exit, ? ? ? "Exit interactive mode"},
>> + ? ? { NULL, NULL, NULL}
>> +};
>> +
>> +static void cmd_help(int argcp, char **argvp)
>> +{
>> + ? ? int i;
>> +
>> + ? ? for (i = 0; commands[i].cmd; i++)
>> + ? ? ? ? ? ? printf("%-12s\t%s\n", commands[i].cmd, commands[i].desc);
>> +}
>> +
>> +static void parse_line(char *line_read)
>> +{
>> + ? ? gchar **argvp;
>> + ? ? int argcp;
>> + ? ? int i;
>> +
>> + ? ? if (line_read == NULL) {
>> + ? ? ? ? ? ? printf("\n");
>> + ? ? ? ? ? ? cmd_exit(0, NULL);
>> + ? ? ? ? ? ? return;
>> + ? ? }
>> +
>> + ? ? line_read = g_strstrip(line_read);
>> +
>> + ? ? if (*line_read == '\0')
>> + ? ? ? ? ? ? return;
>> +
>> + ? ? add_history(line_read);
>> +
>> + ? ? g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
>> +
>> + ? ? for (i = 0; commands[i].cmd; i++)
>> + ? ? ? ? ? ? if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
>> + ? ? ? ? ? ? ? ? ? ? break;
>> +
>> + ? ? if (commands[i].cmd)
>> + ? ? ? ? ? ? commands[i].func(argcp, argvp);
>> + ? ? else
>> + ? ? ? ? ? ? printf("%s: command not found\n", argvp[0]);
>> +
>> + ? ? g_strfreev(argvp);
>> +}
>> +
>> +static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? gpointer user_data)
>> +{
>> + ? ? if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
>> + ? ? ? ? ? ? g_io_channel_unref(chan);
>> + ? ? ? ? ? ? return FALSE;
>> + ? ? }
>> +
>> + ? ? rl_callback_read_char();
>> +
>> + ? ? return TRUE;
>> +}
>> +
>> +int interactive(void)
>> +{
>> + ? ? GIOChannel *pchan;
>> + ? ? gint events;
>> +
>> + ? ? event_loop = g_main_loop_new(NULL, FALSE);
>> +
>> + ? ? pchan = g_io_channel_unix_new(fileno(stdin));
>> + ? ? g_io_channel_set_close_on_unref(pchan, TRUE);
>
> Please add a blank line here.
>
> --
> Gustavo F. Padovan
> http://profusion.mobi
>

Sheldon

2011-02-14 18:13:38

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH v3 2/3] Add an initial interactive mode to gatttool

Hi Sheldon,

* Sheldon Demario <[email protected]> [2011-02-14 12:07:19 -0300]:

> Mode required to allow better GATT procedures control. Some scenarios
> require sequential commands without disconnection and delay between
> operations. It is also desirable to change some connection parameters
> of an active connection.
> ---
> Makefile.am | 8 ++-
> attrib/gatttool.c | 9 ++++
> attrib/gatttool.h | 24 +++++++++
> attrib/interactive.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++
> configure.ac | 1 +
> 5 files changed, 168 insertions(+), 2 deletions(-)
> create mode 100644 attrib/gatttool.h
> create mode 100644 attrib/interactive.c
>
> diff --git a/Makefile.am b/Makefile.am
> index e6639a7..11f990b 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -175,12 +175,16 @@ builtin_sources += plugins/service.c
> endif
>
> if ATTRIBPLUGIN
> +
> +if READLINE

Is that really needed? You already checked for READLINE in ./configure at
this point.

> bin_PROGRAMS += attrib/gatttool
>
> attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
> attrib/gattrib.c btio/btio.c \
> - src/glib-helper.h src/glib-helper.c
> -attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@
> + src/glib-helper.h src/glib-helper.c \
> + attrib/gatttool.h attrib/interactive.c
> +attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @READLINE_LIBS@
> +endif
>
> builtin_modules += attrib
> builtin_sources += attrib/main.c \
> diff --git a/attrib/gatttool.c b/attrib/gatttool.c
> index 8e8ed8e..d663eb5 100644
> --- a/attrib/gatttool.c
> +++ b/attrib/gatttool.c
> @@ -42,6 +42,7 @@
> #include "gattrib.h"
> #include "glib-helper.h"
> #include "gatt.h"
> +#include "gatttool.h"
>
> /* Minimum MTU for L2CAP connections over BR/EDR */
> #define ATT_MIN_MTU_L2CAP 48
> @@ -63,6 +64,7 @@ static gboolean opt_listen = FALSE;
> static gboolean opt_char_desc = FALSE;
> static gboolean opt_le = FALSE;
> static gboolean opt_char_write = FALSE;
> +static gboolean opt_interactive = FALSE;
> static GMainLoop *event_loop;
> static gboolean got_error = FALSE;
>
> @@ -537,6 +539,8 @@ static GOptionEntry gatt_options[] = {
> "Listen for notifications and indications", NULL },
> { "le", 0, 0, G_OPTION_ARG_NONE, &opt_le,
> "Use Bluetooth Low Energy transport", NULL },
> + { "interactive", 'I', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE,
> + &opt_interactive, "Use interactive mode", NULL },
> { NULL },
> };
>
> @@ -594,6 +598,11 @@ int main(int argc, char *argv[])
> g_error_free(gerr);
> }
>
> + if (opt_interactive) {
> + interactive();
> + goto done;
> + }
> +
> if (opt_primary)
> callback = primary;
> else if (opt_characteristics)
> diff --git a/attrib/gatttool.h b/attrib/gatttool.h
> new file mode 100644
> index 0000000..ed5d9d6
> --- /dev/null
> +++ b/attrib/gatttool.h
> @@ -0,0 +1,24 @@
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2011 Nokia Corporation
> + *
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
> + *
> + */
> +
> +int interactive(void);
> diff --git a/attrib/interactive.c b/attrib/interactive.c
> new file mode 100644
> index 0000000..0653609
> --- /dev/null
> +++ b/attrib/interactive.c
> @@ -0,0 +1,128 @@
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2011 Nokia Corporation
> + *
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
> + *
> + */
> +#include <strings.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <glib.h>
> +
> +#include <readline/readline.h>
> +#include <readline/history.h>
> +
> +#include "gatttool.h"
> +
> +static GMainLoop *event_loop;
> +
> +static void cmd_help(int argcp, char **argvp);

Just move cmd_help() up to avoid this.

> +
> +static void cmd_exit(int argcp, char **argvp)
> +{
> + rl_callback_handler_remove();
> + g_main_loop_quit(event_loop);
> +}
> +
> +static struct {
> + const char *cmd;
> + void (*func)(int argcp, char **argvp);
> + const char *desc;
> +} commands[] = {
> + { "help", cmd_help, "Show this help"},
> + { "exit", cmd_exit, "Exit interactive mode"},
> + { NULL, NULL, NULL}
> +};
> +
> +static void cmd_help(int argcp, char **argvp)
> +{
> + int i;
> +
> + for (i = 0; commands[i].cmd; i++)
> + printf("%-12s\t%s\n", commands[i].cmd, commands[i].desc);
> +}
> +
> +static void parse_line(char *line_read)
> +{
> + gchar **argvp;
> + int argcp;
> + int i;
> +
> + if (line_read == NULL) {
> + printf("\n");
> + cmd_exit(0, NULL);
> + return;
> + }
> +
> + line_read = g_strstrip(line_read);
> +
> + if (*line_read == '\0')
> + return;
> +
> + add_history(line_read);
> +
> + g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
> +
> + for (i = 0; commands[i].cmd; i++)
> + if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
> + break;
> +
> + if (commands[i].cmd)
> + commands[i].func(argcp, argvp);
> + else
> + printf("%s: command not found\n", argvp[0]);
> +
> + g_strfreev(argvp);
> +}
> +
> +static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
> + gpointer user_data)
> +{
> + if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
> + g_io_channel_unref(chan);
> + return FALSE;
> + }
> +
> + rl_callback_read_char();
> +
> + return TRUE;
> +}
> +
> +int interactive(void)
> +{
> + GIOChannel *pchan;
> + gint events;
> +
> + event_loop = g_main_loop_new(NULL, FALSE);
> +
> + pchan = g_io_channel_unix_new(fileno(stdin));
> + g_io_channel_set_close_on_unref(pchan, TRUE);

Please add a blank line here.

--
Gustavo F. Padovan
http://profusion.mobi

2011-02-14 15:07:20

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH v3 3/3] Add connect/disconnect options on interactive mode of gatttool

---
attrib/gatttool.c | 10 ++--
attrib/gatttool.h | 3 +-
attrib/interactive.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 119 insertions(+), 8 deletions(-)

diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index d663eb5..cd6b60d 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -83,7 +83,7 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
}
}

-static GIOChannel *do_connect(gboolean le)
+GIOChannel *do_connect(gchar *dst, gboolean le, BtIOConnect connect_cb)
{
GIOChannel *chan;
bdaddr_t sba, dba;
@@ -99,11 +99,11 @@ static GIOChannel *do_connect(gboolean le)
}

/* Remote device */
- if (opt_dst == NULL) {
+ if (dst == NULL) {
g_printerr("Remote Bluetooth address required\n");
return NULL;
}
- str2ba(opt_dst, &dba);
+ str2ba(dst, &dba);

/* Local adapter */
if (opt_src != NULL) {
@@ -599,7 +599,7 @@ int main(int argc, char *argv[])
}

if (opt_interactive) {
- interactive();
+ interactive(opt_dst, opt_le);
goto done;
}

@@ -621,7 +621,7 @@ int main(int argc, char *argv[])
goto done;
}

- chan = do_connect(opt_le);
+ chan = do_connect(opt_dst, opt_le, connect_cb);
if (chan == NULL) {
got_error = TRUE;
goto done;
diff --git a/attrib/gatttool.h b/attrib/gatttool.h
index ed5d9d6..2237330 100644
--- a/attrib/gatttool.h
+++ b/attrib/gatttool.h
@@ -21,4 +21,5 @@
*
*/

-int interactive(void);
+int interactive(gchar *dst, gboolean le);
+GIOChannel *do_connect(gchar *dst, gboolean le, BtIOConnect connect_cb);
diff --git a/attrib/interactive.c b/attrib/interactive.c
index 0653609..83a0778 100644
--- a/attrib/interactive.c
+++ b/attrib/interactive.c
@@ -28,18 +28,119 @@
#include <readline/readline.h>
#include <readline/history.h>

+#include "btio.h"
+#include "gattrib.h"
#include "gatttool.h"

+static GIOChannel *iochannel = NULL;
+static GAttrib *attrib = NULL;
static GMainLoop *event_loop;
+static GString *prompt;
+
+static gchar *opt_dst = NULL;
+static gboolean opt_le = FALSE;

static void cmd_help(int argcp, char **argvp);

+enum state {
+ STATE_DISCONNECTED,
+ STATE_CONNECTING,
+ STATE_CONNECTED
+} conn_state;
+
+static char *get_prompt(void)
+{
+ if (conn_state == STATE_CONNECTING) {
+ g_string_assign(prompt, "Connecting... ");
+ return prompt->str;
+ }
+
+ if (conn_state == STATE_CONNECTED)
+ g_string_assign(prompt, "[CON]");
+ else
+ g_string_assign(prompt, "[ ]");
+
+ if (opt_dst)
+ g_string_append_printf(prompt, "[%17s]", opt_dst);
+ else
+ g_string_append_printf(prompt, "[%17s]", "");
+
+ if (opt_le)
+ g_string_append(prompt, "[LE]");
+ else
+ g_string_append(prompt, "[BR]");
+
+ g_string_append(prompt, "> ");
+
+ return prompt->str;
+}
+
+
+static void set_state(enum state st)
+{
+ conn_state = st;
+ rl_set_prompt(get_prompt());
+ rl_redisplay();
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+ if (err) {
+ printf("connect error: %s\n", err->message);
+ set_state(STATE_DISCONNECTED);
+ return;
+ }
+
+ attrib = g_attrib_new(iochannel);
+ set_state(STATE_CONNECTED);
+}
+
static void cmd_exit(int argcp, char **argvp)
{
rl_callback_handler_remove();
g_main_loop_quit(event_loop);
}

+static void cmd_connect(int argcp, char **argvp)
+{
+ if (conn_state != STATE_DISCONNECTED)
+ return;
+
+ if (argcp > 1) {
+ g_free(opt_dst);
+ opt_dst = strdup(argvp[1]);
+ }
+
+ if (opt_dst == NULL) {
+ printf("Remote Bluetooth address required\n");
+ return;
+ }
+
+ set_state(STATE_CONNECTING);
+ iochannel = do_connect(opt_dst, opt_le, connect_cb);
+ if (iochannel == NULL)
+ set_state(STATE_DISCONNECTED);
+
+ return;
+}
+
+static void cmd_disconnect(int argcp, char **argvp)
+{
+ if (conn_state == STATE_DISCONNECTED)
+ return;
+
+ g_attrib_unref(attrib);
+ attrib = NULL;
+
+ g_io_channel_shutdown(iochannel, FALSE, NULL);
+ g_io_channel_unref(iochannel);
+ iochannel = NULL;
+
+ set_state(STATE_DISCONNECTED);
+
+ return;
+}
+
static struct {
const char *cmd;
void (*func)(int argcp, char **argvp);
@@ -47,6 +148,8 @@ static struct {
} commands[] = {
{ "help", cmd_help, "Show this help"},
{ "exit", cmd_exit, "Exit interactive mode"},
+ { "connect", cmd_connect, "Connect to a remote device"},
+ { "disconnect", cmd_disconnect, "Disconnect from a remote device"},
{ NULL, NULL, NULL}
};

@@ -104,11 +207,16 @@ static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
return TRUE;
}

-int interactive(void)
+int interactive(gchar *dst, gboolean le)
{
GIOChannel *pchan;
gint events;

+ opt_dst = dst;
+ opt_le = le;
+
+ prompt = g_string_new(NULL);
+
event_loop = g_main_loop_new(NULL, FALSE);

pchan = g_io_channel_unix_new(fileno(stdin));
@@ -116,13 +224,15 @@ int interactive(void)
events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
g_io_add_watch(pchan, events, prompt_read, NULL);

- rl_callback_handler_install("> ", parse_line);
+ rl_callback_handler_install(get_prompt(), parse_line);

g_main_loop_run(event_loop);

rl_callback_handler_remove();
+ cmd_disconnect(0, NULL);
g_io_channel_unref(pchan);
g_main_loop_unref(event_loop);
+ g_string_free(prompt, TRUE);

return 0;
}
--
1.7.1


2011-02-14 15:07:19

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH v3 2/3] Add an initial interactive mode to gatttool

Mode required to allow better GATT procedures control. Some scenarios
require sequential commands without disconnection and delay between
operations. It is also desirable to change some connection parameters
of an active connection.
---
Makefile.am | 8 ++-
attrib/gatttool.c | 9 ++++
attrib/gatttool.h | 24 +++++++++
attrib/interactive.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++
configure.ac | 1 +
5 files changed, 168 insertions(+), 2 deletions(-)
create mode 100644 attrib/gatttool.h
create mode 100644 attrib/interactive.c

diff --git a/Makefile.am b/Makefile.am
index e6639a7..11f990b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -175,12 +175,16 @@ builtin_sources += plugins/service.c
endif

if ATTRIBPLUGIN
+
+if READLINE
bin_PROGRAMS += attrib/gatttool

attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
attrib/gattrib.c btio/btio.c \
- src/glib-helper.h src/glib-helper.c
-attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@
+ src/glib-helper.h src/glib-helper.c \
+ attrib/gatttool.h attrib/interactive.c
+attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @READLINE_LIBS@
+endif

builtin_modules += attrib
builtin_sources += attrib/main.c \
diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index 8e8ed8e..d663eb5 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -42,6 +42,7 @@
#include "gattrib.h"
#include "glib-helper.h"
#include "gatt.h"
+#include "gatttool.h"

/* Minimum MTU for L2CAP connections over BR/EDR */
#define ATT_MIN_MTU_L2CAP 48
@@ -63,6 +64,7 @@ static gboolean opt_listen = FALSE;
static gboolean opt_char_desc = FALSE;
static gboolean opt_le = FALSE;
static gboolean opt_char_write = FALSE;
+static gboolean opt_interactive = FALSE;
static GMainLoop *event_loop;
static gboolean got_error = FALSE;

@@ -537,6 +539,8 @@ static GOptionEntry gatt_options[] = {
"Listen for notifications and indications", NULL },
{ "le", 0, 0, G_OPTION_ARG_NONE, &opt_le,
"Use Bluetooth Low Energy transport", NULL },
+ { "interactive", 'I', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE,
+ &opt_interactive, "Use interactive mode", NULL },
{ NULL },
};

@@ -594,6 +598,11 @@ int main(int argc, char *argv[])
g_error_free(gerr);
}

+ if (opt_interactive) {
+ interactive();
+ goto done;
+ }
+
if (opt_primary)
callback = primary;
else if (opt_characteristics)
diff --git a/attrib/gatttool.h b/attrib/gatttool.h
new file mode 100644
index 0000000..ed5d9d6
--- /dev/null
+++ b/attrib/gatttool.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int interactive(void);
diff --git a/attrib/interactive.c b/attrib/interactive.c
new file mode 100644
index 0000000..0653609
--- /dev/null
+++ b/attrib/interactive.c
@@ -0,0 +1,128 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "gatttool.h"
+
+static GMainLoop *event_loop;
+
+static void cmd_help(int argcp, char **argvp);
+
+static void cmd_exit(int argcp, char **argvp)
+{
+ rl_callback_handler_remove();
+ g_main_loop_quit(event_loop);
+}
+
+static struct {
+ const char *cmd;
+ void (*func)(int argcp, char **argvp);
+ const char *desc;
+} commands[] = {
+ { "help", cmd_help, "Show this help"},
+ { "exit", cmd_exit, "Exit interactive mode"},
+ { NULL, NULL, NULL}
+};
+
+static void cmd_help(int argcp, char **argvp)
+{
+ int i;
+
+ for (i = 0; commands[i].cmd; i++)
+ printf("%-12s\t%s\n", commands[i].cmd, commands[i].desc);
+}
+
+static void parse_line(char *line_read)
+{
+ gchar **argvp;
+ int argcp;
+ int i;
+
+ if (line_read == NULL) {
+ printf("\n");
+ cmd_exit(0, NULL);
+ return;
+ }
+
+ line_read = g_strstrip(line_read);
+
+ if (*line_read == '\0')
+ return;
+
+ add_history(line_read);
+
+ g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
+
+ for (i = 0; commands[i].cmd; i++)
+ if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
+ break;
+
+ if (commands[i].cmd)
+ commands[i].func(argcp, argvp);
+ else
+ printf("%s: command not found\n", argvp[0]);
+
+ g_strfreev(argvp);
+}
+
+static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ g_io_channel_unref(chan);
+ return FALSE;
+ }
+
+ rl_callback_read_char();
+
+ return TRUE;
+}
+
+int interactive(void)
+{
+ GIOChannel *pchan;
+ gint events;
+
+ event_loop = g_main_loop_new(NULL, FALSE);
+
+ pchan = g_io_channel_unix_new(fileno(stdin));
+ g_io_channel_set_close_on_unref(pchan, TRUE);
+ events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ g_io_add_watch(pchan, events, prompt_read, NULL);
+
+ rl_callback_handler_install("> ", parse_line);
+
+ g_main_loop_run(event_loop);
+
+ rl_callback_handler_remove();
+ g_io_channel_unref(pchan);
+ g_main_loop_unref(event_loop);
+
+ return 0;
+}
diff --git a/configure.ac b/configure.ac
index bebdc9c..a8e8fc6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,7 @@ AC_PATH_GSTREAMER
AC_PATH_USB
AC_PATH_SNDFILE
AC_PATH_OUI
+AC_PATH_READLINE

AC_ARG_BLUEZ

--
1.7.1


2011-02-14 15:07:18

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH v3 1/3] Include check to readline lib on acinlude.m4

---
acinclude.m4 | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/acinclude.m4 b/acinclude.m4
index 9d3f6b2..91e0956 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -153,6 +153,15 @@ AC_DEFUN([AC_PATH_SNDFILE], [
AC_SUBST(SNDFILE_LIBS)
])

+AC_DEFUN([AC_PATH_READLINE], [
+ AC_CHECK_HEADER(readline/readline.h,
+ AC_CHECK_LIB(readline, main,
+ [ readline_found=yes
+ AC_SUBST(READLINE_LIBS, "-lreadline")
+ ], readline_found=no),
+ [])
+])
+
AC_DEFUN([AC_PATH_OUI], [
AC_ARG_WITH(ouifile,
AS_HELP_STRING([--with-ouifile=PATH],[Path to the oui.txt file @<:@auto@:>@]),
@@ -356,6 +365,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
AM_CONDITIONAL(HEALTHPLUGIN, test "${health_enable}" = "yes")
AM_CONDITIONAL(MCAP, test "${health_enable}" = "yes")
AM_CONDITIONAL(HAL, test "${hal_enable}" = "yes")
+ AM_CONDITIONAL(READLINE, test "${readline_found}" = "yes")
AM_CONDITIONAL(ATTRIBPLUGIN, test "${attrib_enable}" = "yes")
AM_CONDITIONAL(ECHOPLUGIN, test "no" = "yes")
AM_CONDITIONAL(PNATPLUGIN, test "${pnat_enable}" = "yes")
--
1.7.1


2011-02-11 14:17:23

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH v2 5/5] Add connect/disconnect options on interactive mode of gatttool

---
attrib/gatttool.c | 6 +-
attrib/gatttool.h | 3 +-
attrib/interactive.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 108 insertions(+), 6 deletions(-)

diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index badd3b0..1b89d9f 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -87,7 +87,7 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
}
}

-static GIOChannel *do_connect(gboolean le)
+GIOChannel *do_connect(gboolean le, BtIOConnect connect_cb)
{
GIOChannel *chan;
bdaddr_t sba, dba;
@@ -609,7 +609,7 @@ int main(int argc, char *argv[])
}

if (main_opts.interactive) {
- interactive();
+ interactive(&main_opts);
goto done;
}

@@ -631,7 +631,7 @@ int main(int argc, char *argv[])
goto done;
}

- chan = do_connect(main_opts.le);
+ chan = do_connect(main_opts.le, connect_cb);
if (chan == NULL) {
got_error = TRUE;
goto done;
diff --git a/attrib/gatttool.h b/attrib/gatttool.h
index 3dbe88b..c80026c 100644
--- a/attrib/gatttool.h
+++ b/attrib/gatttool.h
@@ -42,4 +42,5 @@ struct main_opts {
gboolean interactive;
};

-int interactive(void);
+int interactive(struct main_opts *config);
+GIOChannel *do_connect(gboolean le, BtIOConnect connect_cb);
diff --git a/attrib/interactive.c b/attrib/interactive.c
index 425a9b2..5fb6f8e 100644
--- a/attrib/interactive.c
+++ b/attrib/interactive.c
@@ -30,18 +30,111 @@
#include <readline/readline.h>
#include <readline/history.h>

+#include "btio.h"
+#include "gattrib.h"
#include "gatttool.h"

+static GIOChannel *iochannel = NULL;
+static GAttrib *attrib = NULL;
static GMainLoop *event_loop;
+static GString *prompt;
+static struct main_opts *main_opts;

static void cmd_help(int argcp, char **argvp);

+enum state {
+ STATE_DISCONNECTED,
+ STATE_CONNECTING,
+ STATE_CONNECTED
+} conn_state;
+
+static char *get_prompt(void)
+{
+ if (conn_state == STATE_CONNECTING) {
+ g_string_assign(prompt, "Connecting... ");
+ return prompt->str;
+ }
+
+ if (conn_state == STATE_CONNECTED)
+ g_string_assign(prompt, "[CON]");
+ else
+ g_string_assign(prompt, "[ ]");
+
+ if (main_opts->dst)
+ g_string_append_printf(prompt, "[%17s]", main_opts->dst);
+ else
+ g_string_append_printf(prompt, "[%17s]", "");
+
+ if (main_opts->le)
+ g_string_append(prompt, "[LE]");
+ else
+ g_string_append(prompt, "[BR]");
+
+ g_string_append(prompt, "> ");
+
+ return prompt->str;
+}
+
+
+static void set_state(enum state st)
+{
+ conn_state = st;
+ rl_set_prompt(get_prompt());
+ rl_redisplay();
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+ if (err) {
+ printf("connect error: %s\n", err->message);
+ return;
+ }
+
+ attrib = g_attrib_new(iochannel);
+ set_state(STATE_CONNECTED);
+}
+
static void cmd_exit(int argcp, char **argvp)
{
rl_callback_handler_remove();
g_main_loop_quit(event_loop);
}

+static void cmd_connect(int argcp, char **argvp)
+{
+ if (conn_state != STATE_DISCONNECTED)
+ return;
+
+ if (main_opts->dst == NULL) {
+ printf("Remote Bluetooth address required\n");
+ return;
+ }
+
+ set_state(STATE_CONNECTING);
+ iochannel = do_connect(main_opts->le, connect_cb);
+ if (iochannel == NULL)
+ set_state(STATE_DISCONNECTED);
+
+ return;
+}
+
+static void cmd_disconnect(int argcp, char **argvp)
+{
+ if (conn_state == STATE_DISCONNECTED)
+ return;
+
+ g_attrib_unref(attrib);
+ attrib = NULL;
+
+ g_io_channel_shutdown(iochannel, FALSE, NULL);
+ g_io_channel_unref(iochannel);
+ iochannel = NULL;
+
+ set_state(STATE_DISCONNECTED);
+
+ return;
+}
+
static struct {
const char *cmd;
void (*func)(int argcp, char **argvp);
@@ -49,6 +142,8 @@ static struct {
} commands[] = {
{ "help", cmd_help, "Show this help"},
{ "exit", cmd_exit, "Exit interactive mode"},
+ { "connect", cmd_connect, "Connect to a remote device"},
+ { "disconnect", cmd_disconnect, "Disconnect from a remote device"},
{ NULL, NULL, NULL}
};

@@ -106,11 +201,15 @@ static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
return TRUE;
}

-int interactive(void)
+int interactive(struct main_opts *config)
{
GIOChannel *pchan;
gint events;

+ main_opts = config;
+
+ prompt = g_string_new(NULL);
+
event_loop = g_main_loop_new(NULL, FALSE);

pchan = g_io_channel_unix_new(fileno(stdin));
@@ -118,13 +217,15 @@ int interactive(void)
events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
g_io_add_watch(pchan, events, prompt_read, NULL);

- rl_callback_handler_install("> ", parse_line);
+ rl_callback_handler_install(get_prompt(), parse_line);

g_main_loop_run(event_loop);

rl_callback_handler_remove();
+ cmd_disconnect(0, NULL);
g_io_channel_unref(pchan);
g_main_loop_unref(event_loop);
+ g_string_free(prompt, TRUE);

return 0;
}
--
1.7.1


2011-02-11 14:17:22

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH v2 4/5] Move main_opts from gatttool.c to header file

---
attrib/gatttool.c | 22 +---------------------
attrib/gatttool.h | 21 +++++++++++++++++++++
attrib/interactive.c | 2 ++
3 files changed, 24 insertions(+), 21 deletions(-)

diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index cd6e68a..badd3b0 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -49,27 +49,7 @@

static GMainLoop *event_loop;
static gboolean got_error = FALSE;
-
-static struct main_opts {
- gchar *src;
- gchar *dst;
- gchar *value;
- gchar *sec_level;
- uuid_t *uuid;
- int start;
- int end;
- int handle;
- int mtu;
- int psm;
- gboolean primary;
- gboolean char_disc;
- gboolean char_read;
- gboolean listen;
- gboolean char_desc;
- gboolean le;
- gboolean char_write;
- gboolean interactive;
-} main_opts;
+static struct main_opts main_opts;

struct characteristic_data {
GAttrib *attrib;
diff --git a/attrib/gatttool.h b/attrib/gatttool.h
index ed5d9d6..3dbe88b 100644
--- a/attrib/gatttool.h
+++ b/attrib/gatttool.h
@@ -21,4 +21,25 @@
*
*/

+struct main_opts {
+ gchar *src;
+ gchar *dst;
+ gchar *value;
+ gchar *sec_level;
+ uuid_t *uuid;
+ int start;
+ int end;
+ int handle;
+ int mtu;
+ int psm;
+ gboolean primary;
+ gboolean char_disc;
+ gboolean char_read;
+ gboolean listen;
+ gboolean char_desc;
+ gboolean le;
+ gboolean char_write;
+ gboolean interactive;
+};
+
int interactive(void);
diff --git a/attrib/interactive.c b/attrib/interactive.c
index 0653609..425a9b2 100644
--- a/attrib/interactive.c
+++ b/attrib/interactive.c
@@ -25,6 +25,8 @@
#include <stdio.h>
#include <glib.h>

+#include <bluetooth/sdp.h>
+
#include <readline/readline.h>
#include <readline/history.h>

--
1.7.1


2011-02-11 14:17:21

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH v2 3/5] Add an initial interactive mode to gatttool

Mode required to allow better GATT procedures control. Some scenarios
require sequential commands without disconnection and delay between
operations. It is also desirable to change some connection parameters
of an active connection.
---
Makefile.am | 8 ++-
attrib/gatttool.c | 9 ++++
attrib/gatttool.h | 24 +++++++++
attrib/interactive.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++
configure.ac | 1 +
5 files changed, 168 insertions(+), 2 deletions(-)
create mode 100644 attrib/gatttool.h
create mode 100644 attrib/interactive.c

diff --git a/Makefile.am b/Makefile.am
index e6639a7..11f990b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -175,12 +175,16 @@ builtin_sources += plugins/service.c
endif

if ATTRIBPLUGIN
+
+if READLINE
bin_PROGRAMS += attrib/gatttool

attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
attrib/gattrib.c btio/btio.c \
- src/glib-helper.h src/glib-helper.c
-attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@
+ src/glib-helper.h src/glib-helper.c \
+ attrib/gatttool.h attrib/interactive.c
+attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @READLINE_LIBS@
+endif

builtin_modules += attrib
builtin_sources += attrib/main.c \
diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index c612309..cd6e68a 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -42,6 +42,7 @@
#include "gattrib.h"
#include "glib-helper.h"
#include "gatt.h"
+#include "gatttool.h"

/* Minimum MTU for L2CAP connections over BR/EDR */
#define ATT_MIN_MTU_L2CAP 48
@@ -67,6 +68,7 @@ static struct main_opts {
gboolean char_desc;
gboolean le;
gboolean char_write;
+ gboolean interactive;
} main_opts;

struct characteristic_data {
@@ -565,6 +567,8 @@ static GOptionEntry gatt_options[] = {
"Listen for notifications and indications", NULL },
{ "le", 0, 0, G_OPTION_ARG_NONE, &main_opts.le,
"Use Bluetooth Low Energy transport", NULL },
+ { "interactive", 'I', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE,
+ &main_opts.interactive, "Use interactive mode", NULL },
{ NULL },
};

@@ -624,6 +628,11 @@ int main(int argc, char *argv[])
g_error_free(gerr);
}

+ if (main_opts.interactive) {
+ interactive();
+ goto done;
+ }
+
if (main_opts.primary)
callback = primary;
else if (main_opts.char_disc)
diff --git a/attrib/gatttool.h b/attrib/gatttool.h
new file mode 100644
index 0000000..ed5d9d6
--- /dev/null
+++ b/attrib/gatttool.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int interactive(void);
diff --git a/attrib/interactive.c b/attrib/interactive.c
new file mode 100644
index 0000000..0653609
--- /dev/null
+++ b/attrib/interactive.c
@@ -0,0 +1,128 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "gatttool.h"
+
+static GMainLoop *event_loop;
+
+static void cmd_help(int argcp, char **argvp);
+
+static void cmd_exit(int argcp, char **argvp)
+{
+ rl_callback_handler_remove();
+ g_main_loop_quit(event_loop);
+}
+
+static struct {
+ const char *cmd;
+ void (*func)(int argcp, char **argvp);
+ const char *desc;
+} commands[] = {
+ { "help", cmd_help, "Show this help"},
+ { "exit", cmd_exit, "Exit interactive mode"},
+ { NULL, NULL, NULL}
+};
+
+static void cmd_help(int argcp, char **argvp)
+{
+ int i;
+
+ for (i = 0; commands[i].cmd; i++)
+ printf("%-12s\t%s\n", commands[i].cmd, commands[i].desc);
+}
+
+static void parse_line(char *line_read)
+{
+ gchar **argvp;
+ int argcp;
+ int i;
+
+ if (line_read == NULL) {
+ printf("\n");
+ cmd_exit(0, NULL);
+ return;
+ }
+
+ line_read = g_strstrip(line_read);
+
+ if (*line_read == '\0')
+ return;
+
+ add_history(line_read);
+
+ g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
+
+ for (i = 0; commands[i].cmd; i++)
+ if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
+ break;
+
+ if (commands[i].cmd)
+ commands[i].func(argcp, argvp);
+ else
+ printf("%s: command not found\n", argvp[0]);
+
+ g_strfreev(argvp);
+}
+
+static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ g_io_channel_unref(chan);
+ return FALSE;
+ }
+
+ rl_callback_read_char();
+
+ return TRUE;
+}
+
+int interactive(void)
+{
+ GIOChannel *pchan;
+ gint events;
+
+ event_loop = g_main_loop_new(NULL, FALSE);
+
+ pchan = g_io_channel_unix_new(fileno(stdin));
+ g_io_channel_set_close_on_unref(pchan, TRUE);
+ events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ g_io_add_watch(pchan, events, prompt_read, NULL);
+
+ rl_callback_handler_install("> ", parse_line);
+
+ g_main_loop_run(event_loop);
+
+ rl_callback_handler_remove();
+ g_io_channel_unref(pchan);
+ g_main_loop_unref(event_loop);
+
+ return 0;
+}
diff --git a/configure.ac b/configure.ac
index bebdc9c..a8e8fc6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,7 @@ AC_PATH_GSTREAMER
AC_PATH_USB
AC_PATH_SNDFILE
AC_PATH_OUI
+AC_PATH_READLINE

AC_ARG_BLUEZ

--
1.7.1


2011-02-11 14:17:20

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH v2 2/5] Include check to readline lib on acinlude.m4

---
acinclude.m4 | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/acinclude.m4 b/acinclude.m4
index 9d3f6b2..91e0956 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -153,6 +153,15 @@ AC_DEFUN([AC_PATH_SNDFILE], [
AC_SUBST(SNDFILE_LIBS)
])

+AC_DEFUN([AC_PATH_READLINE], [
+ AC_CHECK_HEADER(readline/readline.h,
+ AC_CHECK_LIB(readline, main,
+ [ readline_found=yes
+ AC_SUBST(READLINE_LIBS, "-lreadline")
+ ], readline_found=no),
+ [])
+])
+
AC_DEFUN([AC_PATH_OUI], [
AC_ARG_WITH(ouifile,
AS_HELP_STRING([--with-ouifile=PATH],[Path to the oui.txt file @<:@auto@:>@]),
@@ -356,6 +365,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
AM_CONDITIONAL(HEALTHPLUGIN, test "${health_enable}" = "yes")
AM_CONDITIONAL(MCAP, test "${health_enable}" = "yes")
AM_CONDITIONAL(HAL, test "${hal_enable}" = "yes")
+ AM_CONDITIONAL(READLINE, test "${readline_found}" = "yes")
AM_CONDITIONAL(ATTRIBPLUGIN, test "${attrib_enable}" = "yes")
AM_CONDITIONAL(ECHOPLUGIN, test "no" = "yes")
AM_CONDITIONAL(PNATPLUGIN, test "${pnat_enable}" = "yes")
--
1.7.1


2011-02-11 14:17:19

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH v2 1/5] Put all gatttool global options in a unique struct

---
attrib/gatttool.c | 186 +++++++++++++++++++++++++++++++----------------------
1 files changed, 108 insertions(+), 78 deletions(-)

diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index 8e8ed8e..c612309 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -46,32 +46,56 @@
/* Minimum MTU for L2CAP connections over BR/EDR */
#define ATT_MIN_MTU_L2CAP 48

-static gchar *opt_src = NULL;
-static gchar *opt_dst = NULL;
-static gchar *opt_value = NULL;
-static gchar *opt_sec_level = "low";
-static uuid_t *opt_uuid = NULL;
-static int opt_start = 0x0001;
-static int opt_end = 0xffff;
-static int opt_handle = -1;
-static int opt_mtu = 0;
-static int opt_psm = 0x1f;
-static gboolean opt_primary = FALSE;
-static gboolean opt_characteristics = FALSE;
-static gboolean opt_char_read = FALSE;
-static gboolean opt_listen = FALSE;
-static gboolean opt_char_desc = FALSE;
-static gboolean opt_le = FALSE;
-static gboolean opt_char_write = FALSE;
static GMainLoop *event_loop;
static gboolean got_error = FALSE;

+static struct main_opts {
+ gchar *src;
+ gchar *dst;
+ gchar *value;
+ gchar *sec_level;
+ uuid_t *uuid;
+ int start;
+ int end;
+ int handle;
+ int mtu;
+ int psm;
+ gboolean primary;
+ gboolean char_disc;
+ gboolean char_read;
+ gboolean listen;
+ gboolean char_desc;
+ gboolean le;
+ gboolean char_write;
+} main_opts;
+
struct characteristic_data {
GAttrib *attrib;
uint16_t start;
uint16_t end;
};

+static void options_init(void)
+{
+ main_opts.src = NULL;
+ main_opts.dst = NULL;
+ main_opts.value = NULL;
+ main_opts.sec_level = "low";
+ main_opts.uuid = NULL;
+ main_opts.start = 0x0001;
+ main_opts.end = 0xffff;
+ main_opts.handle = -1;
+ main_opts.mtu = 0;
+ main_opts.psm = 0x1f;
+ main_opts.primary = FALSE;
+ main_opts.char_disc = FALSE;
+ main_opts.char_read = FALSE;
+ main_opts.listen = FALSE;
+ main_opts.char_desc = FALSE;
+ main_opts.le = FALSE;
+ main_opts.char_write = FALSE;
+}
+
static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
{
if (err) {
@@ -90,31 +114,31 @@ static GIOChannel *do_connect(gboolean le)

/* This check is required because currently setsockopt() returns no
* errors for MTU values smaller than the allowed minimum. */
- if (opt_mtu != 0 && opt_mtu < ATT_MIN_MTU_L2CAP) {
+ if (main_opts.mtu != 0 && main_opts.mtu < ATT_MIN_MTU_L2CAP) {
g_printerr("MTU cannot be smaller than %d\n",
ATT_MIN_MTU_L2CAP);
return NULL;
}

/* Remote device */
- if (opt_dst == NULL) {
+ if (main_opts.dst == NULL) {
g_printerr("Remote Bluetooth address required\n");
return NULL;
}
- str2ba(opt_dst, &dba);
+ str2ba(main_opts.dst, &dba);

/* Local adapter */
- if (opt_src != NULL) {
- if (!strncmp(opt_src, "hci", 3))
- hci_devba(atoi(opt_src + 3), &sba);
+ if (main_opts.src != NULL) {
+ if (!strncmp(main_opts.src, "hci", 3))
+ hci_devba(atoi(main_opts.src + 3), &sba);
else
- str2ba(opt_src, &sba);
+ str2ba(main_opts.src, &sba);
} else
bacpy(&sba, BDADDR_ANY);

- if (strcmp(opt_sec_level, "medium") == 0)
+ if (strcmp(main_opts.sec_level, "medium") == 0)
sec_level = BT_IO_SEC_MEDIUM;
- else if (strcmp(opt_sec_level, "high") == 0)
+ else if (strcmp(main_opts.sec_level, "high") == 0)
sec_level = BT_IO_SEC_HIGH;
else
sec_level = BT_IO_SEC_LOW;
@@ -124,15 +148,15 @@ static GIOChannel *do_connect(gboolean le)
BT_IO_OPT_SOURCE_BDADDR, &sba,
BT_IO_OPT_DEST_BDADDR, &dba,
BT_IO_OPT_CID, GATT_CID,
- BT_IO_OPT_OMTU, opt_mtu,
+ BT_IO_OPT_OMTU, main_opts.mtu,
BT_IO_OPT_SEC_LEVEL, sec_level,
BT_IO_OPT_INVALID);
else
chan = bt_io_connect(BT_IO_L2CAP, connect_cb, NULL, NULL, &err,
BT_IO_OPT_SOURCE_BDADDR, &sba,
BT_IO_OPT_DEST_BDADDR, &dba,
- BT_IO_OPT_PSM, opt_psm,
- BT_IO_OPT_OMTU, opt_mtu,
+ BT_IO_OPT_PSM, main_opts.psm,
+ BT_IO_OPT_OMTU, main_opts.mtu,
BT_IO_OPT_SEC_LEVEL, sec_level,
BT_IO_OPT_INVALID);

@@ -236,9 +260,9 @@ static gboolean primary(gpointer user_data)
{
GAttrib *attrib = user_data;

- if (opt_uuid)
- gatt_discover_primary(attrib, opt_uuid, primary_by_uuid_cb,
- NULL);
+ if (main_opts.uuid)
+ gatt_discover_primary(attrib, main_opts.uuid,
+ primary_by_uuid_cb, NULL);
else
gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);

@@ -272,7 +296,8 @@ static gboolean characteristics(gpointer user_data)
{
GAttrib *attrib = user_data;

- gatt_discover_char(attrib, opt_start, opt_end, char_discovered_cb, NULL);
+ gatt_discover_char(attrib, main_opts.start, main_opts.end,
+ char_discovered_cb, NULL);

return FALSE;
}
@@ -298,7 +323,7 @@ static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
g_print("\n");

done:
- if (opt_listen == FALSE)
+ if (main_opts.listen == FALSE)
g_main_loop_quit(event_loop);
}

@@ -310,7 +335,7 @@ static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
int i;

if (status == ATT_ECODE_ATTR_NOT_FOUND &&
- char_data->start != opt_start)
+ char_data->start != main_opts.start)
goto done;

if (status != 0) {
@@ -339,7 +364,7 @@ static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
att_data_list_free(list);

gatt_read_char_by_uuid(char_data->attrib, char_data->start,
- char_data->end, opt_uuid,
+ char_data->end, main_opts.uuid,
char_read_by_uuid_cb,
char_data);

@@ -353,27 +378,28 @@ static gboolean characteristics_read(gpointer user_data)
{
GAttrib *attrib = user_data;

- if (opt_uuid != NULL) {
+ if (main_opts.uuid != NULL) {
struct characteristic_data *char_data;

char_data = g_new(struct characteristic_data, 1);
char_data->attrib = attrib;
- char_data->start = opt_start;
- char_data->end = opt_end;
+ char_data->start = main_opts.start;
+ char_data->end = main_opts.end;

- gatt_read_char_by_uuid(attrib, opt_start, opt_end, opt_uuid,
- char_read_by_uuid_cb, char_data);
+ gatt_read_char_by_uuid(attrib, main_opts.start, main_opts.end,
+ main_opts.uuid, char_read_by_uuid_cb,
+ char_data);

return FALSE;
}

- if (opt_handle <= 0) {
+ if (main_opts.handle <= 0) {
g_printerr("A valid handle is required\n");
g_main_loop_quit(event_loop);
return FALSE;
}

- gatt_read_char(attrib, opt_handle, char_read_cb, attrib);
+ gatt_read_char(attrib, main_opts.handle, char_read_cb, attrib);

return FALSE;
}
@@ -411,23 +437,24 @@ static gboolean characteristics_write(gpointer user_data)
uint8_t *value;
size_t len;

- if (opt_handle <= 0) {
+ if (main_opts.handle <= 0) {
g_printerr("A valid handle is required\n");
goto error;
}

- if (opt_value == NULL || opt_value[0] == '\0') {
+ if (main_opts.value == NULL || main_opts.value[0] == '\0') {
g_printerr("A value is required\n");
goto error;
}

- len = attr_data_from_string(opt_value, &value);
+ len = attr_data_from_string(main_opts.value, &value);
if (len == 0) {
g_printerr("Invalid value\n");
goto error;
}

- gatt_write_cmd(attrib, opt_handle, value, len, mainloop_quit, value);
+ gatt_write_cmd(attrib, main_opts.handle, value, len, mainloop_quit,
+ value);

return FALSE;

@@ -474,7 +501,7 @@ static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen,
att_data_list_free(list);

done:
- if (opt_listen == FALSE)
+ if (main_opts.listen == FALSE)
g_main_loop_quit(event_loop);
}

@@ -482,7 +509,8 @@ static gboolean characteristics_desc(gpointer user_data)
{
GAttrib *attrib = user_data;

- gatt_find_info(attrib, opt_start, opt_end, char_desc_cb, NULL);
+ gatt_find_info(attrib, main_opts.start, main_opts.end,
+ char_desc_cb, NULL);

return FALSE;
}
@@ -493,20 +521,20 @@ static gboolean parse_uuid(const char *key, const char *value,
if (!value)
return FALSE;

- opt_uuid = g_try_malloc(sizeof(uuid_t));
- if (opt_uuid == NULL)
+ main_opts.uuid = g_try_malloc(sizeof(uuid_t));
+ if (main_opts.uuid == NULL)
return FALSE;

- if (bt_string2uuid(opt_uuid, value) < 0)
+ if (bt_string2uuid(main_opts.uuid, value) < 0)
return FALSE;

return TRUE;
}

static GOptionEntry primary_char_options[] = {
- { "start", 's' , 0, G_OPTION_ARG_INT, &opt_start,
+ { "start", 's' , 0, G_OPTION_ARG_INT, &main_opts.start,
"Starting handle(optional)", "0x0001" },
- { "end", 'e' , 0, G_OPTION_ARG_INT, &opt_end,
+ { "end", 'e' , 0, G_OPTION_ARG_INT, &main_opts.end,
"Ending handle(optional)", "0xffff" },
{ "uuid", 'u', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
parse_uuid, "UUID16 or UUID128(optional)", "0x1801"},
@@ -514,42 +542,42 @@ static GOptionEntry primary_char_options[] = {
};

static GOptionEntry char_rw_options[] = {
- { "handle", 'a' , 0, G_OPTION_ARG_INT, &opt_handle,
+ { "handle", 'a' , 0, G_OPTION_ARG_INT, &main_opts.handle,
"Read/Write characteristic by handle(required)", "0x0001" },
- { "value", 'n' , 0, G_OPTION_ARG_STRING, &opt_value,
+ { "value", 'n' , 0, G_OPTION_ARG_STRING, &main_opts.value,
"Write characteristic value (required for write operation)",
"0x0001" },
{NULL},
};

static GOptionEntry gatt_options[] = {
- { "primary", 0, 0, G_OPTION_ARG_NONE, &opt_primary,
+ { "primary", 0, 0, G_OPTION_ARG_NONE, &main_opts.primary,
"Primary Service Discovery", NULL },
- { "characteristics", 0, 0, G_OPTION_ARG_NONE, &opt_characteristics,
+ { "characteristics", 0, 0, G_OPTION_ARG_NONE, &main_opts.char_disc,
"Characteristics Discovery", NULL },
- { "char-read", 0, 0, G_OPTION_ARG_NONE, &opt_char_read,
+ { "char-read", 0, 0, G_OPTION_ARG_NONE, &main_opts.char_read,
"Characteristics Value/Descriptor Read", NULL },
- { "char-write", 0, 0, G_OPTION_ARG_NONE, &opt_char_write,
+ { "char-write", 0, 0, G_OPTION_ARG_NONE, &main_opts.char_write,
"Characteristics Value Write", NULL },
- { "char-desc", 0, 0, G_OPTION_ARG_NONE, &opt_char_desc,
+ { "char-desc", 0, 0, G_OPTION_ARG_NONE, &main_opts.char_desc,
"Characteristics Descriptor Discovery", NULL },
- { "listen", 0, 0, G_OPTION_ARG_NONE, &opt_listen,
+ { "listen", 0, 0, G_OPTION_ARG_NONE, &main_opts.listen,
"Listen for notifications and indications", NULL },
- { "le", 0, 0, G_OPTION_ARG_NONE, &opt_le,
+ { "le", 0, 0, G_OPTION_ARG_NONE, &main_opts.le,
"Use Bluetooth Low Energy transport", NULL },
{ NULL },
};

static GOptionEntry options[] = {
- { "adapter", 'i', 0, G_OPTION_ARG_STRING, &opt_src,
+ { "adapter", 'i', 0, G_OPTION_ARG_STRING, &main_opts.src,
"Specify local adapter interface", "hciX" },
- { "device", 'b', 0, G_OPTION_ARG_STRING, &opt_dst,
+ { "device", 'b', 0, G_OPTION_ARG_STRING, &main_opts.dst,
"Specify remote Bluetooth address", "MAC" },
- { "mtu", 'm', 0, G_OPTION_ARG_INT, &opt_mtu,
+ { "mtu", 'm', 0, G_OPTION_ARG_INT, &main_opts.mtu,
"Specify the MTU size", "MTU" },
- { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
+ { "psm", 'p', 0, G_OPTION_ARG_INT, &main_opts.psm,
"Specify the PSM for GATT/ATT over BR/EDR", "PSM" },
- { "sec-level", 'l', 0, G_OPTION_ARG_STRING, &opt_sec_level,
+ { "sec-level", 'l', 0, G_OPTION_ARG_STRING, &main_opts.sec_level,
"Set security level. Default: low", "[low | medium | high]"},
{ NULL },
};
@@ -563,6 +591,8 @@ int main(int argc, char *argv[])
GIOChannel *chan;
GSourceFunc callback;

+ options_init();
+
context = g_option_context_new(NULL);
g_option_context_add_main_entries(context, options, NULL);

@@ -594,15 +624,15 @@ int main(int argc, char *argv[])
g_error_free(gerr);
}

- if (opt_primary)
+ if (main_opts.primary)
callback = primary;
- else if (opt_characteristics)
+ else if (main_opts.char_disc)
callback = characteristics;
- else if (opt_char_read)
+ else if (main_opts.char_read)
callback = characteristics_read;
- else if (opt_char_write)
+ else if (main_opts.char_write)
callback = characteristics_write;
- else if (opt_char_desc)
+ else if (main_opts.char_desc)
callback = characteristics_desc;
else {
gchar *help = g_option_context_get_help(context, TRUE, NULL);
@@ -612,7 +642,7 @@ int main(int argc, char *argv[])
goto done;
}

- chan = do_connect(opt_le);
+ chan = do_connect(main_opts.le);
if (chan == NULL) {
got_error = TRUE;
goto done;
@@ -622,7 +652,7 @@ int main(int argc, char *argv[])

event_loop = g_main_loop_new(NULL, FALSE);

- if (opt_listen)
+ if (main_opts.listen)
g_idle_add(listen_start, attrib);

g_idle_add(callback, attrib);
@@ -638,9 +668,9 @@ int main(int argc, char *argv[])

done:
g_option_context_free(context);
- g_free(opt_src);
- g_free(opt_dst);
- g_free(opt_uuid);
+ g_free(main_opts.src);
+ g_free(main_opts.dst);
+ g_free(main_opts.uuid);

if (got_error)
exit(EXIT_FAILURE);
--
1.7.1


2011-02-10 17:23:48

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH 3/5] Add an initial interactive mode to gatttool

Hi Sheldon,

On Thu, Feb 10, 2011, Sheldon Demario wrote:
> >> +static void cmd_help(int argcp, char **argvp);
> >> +static void cmd_exit(int argcp, char **argvp);
> >
> > No forward-declarations please. Just reorder the functions so that you
> > don't need to do this.
>
> The problem is the cmd_help() function. It must be declared to be
> known by commands[] array.
> The commands[] array also must be known by cmd_help(), so what do you
> suggest to solve this?

For that one a forward-declaration is fine.

Johan

2011-02-10 17:14:33

by Sheldon Demario

[permalink] [raw]
Subject: Re: [PATCH 3/5] Add an initial interactive mode to gatttool

Hi Johan,

>> +static void cmd_help(int argcp, char **argvp);
>> +static void cmd_exit(int argcp, char **argvp);
>
> No forward-declarations please. Just reorder the functions so that you
> don't need to do this.

The problem is the cmd_help() function. It must be declared to be
known by commands[] array.
The commands[] array also must be known by cmd_help(), so what do you
suggest to solve this?

Sheldon

>
> Johan
>

2011-02-09 21:26:42

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH 3/5] Add an initial interactive mode to gatttool

On Tue, Feb 08, 2011, Sheldon Demario wrote:
> +int do_interactive(void);

I don't really like having an exported function called do_*. The places
I've seen this naming convention have usually been when there's some
other function already with the name you want and in all places the do_
variant is static. How about simply interactive() or interactive_mode().
Also, shouldn't you be passing the minimum config options as parameters
to this function? Otherwise you'll have to have some sort of global
config variables.

> +++ b/attrib/igatttool.c

Since we don't have igattool anymore, how about calling this
interactive.c?

> +static void cmd_help(int argcp, char **argvp);
> +static void cmd_exit(int argcp, char **argvp);

No forward-declarations please. Just reorder the functions so that you
don't need to do this.

Johan

2011-02-08 15:32:16

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH 5/5] Add connect/disconnect options on interactive mode of gatttool

---
attrib/gatttool.c | 4 +-
attrib/gatttool.h | 1 +
attrib/igatttool.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 105 insertions(+), 3 deletions(-)

diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index e0de05b..ac9bb60 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -86,7 +86,7 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
}
}

-static GIOChannel *do_connect(gboolean le)
+GIOChannel *do_connect(gboolean le, BtIOConnect connect_cb)
{
GIOChannel *chan;
bdaddr_t sba, dba;
@@ -630,7 +630,7 @@ int main(int argc, char *argv[])
goto done;
}

- chan = do_connect(main_opts.le);
+ chan = do_connect(main_opts.le, connect_cb);
if (chan == NULL) {
got_error = TRUE;
goto done;
diff --git a/attrib/gatttool.h b/attrib/gatttool.h
index 0f78aa8..6fec2b0 100644
--- a/attrib/gatttool.h
+++ b/attrib/gatttool.h
@@ -43,3 +43,4 @@ struct main_opts {
} main_opts;

int do_interactive(void);
+GIOChannel *do_connect(gboolean le, BtIOConnect connect_cb);
diff --git a/attrib/igatttool.c b/attrib/igatttool.c
index cac97f0..fa014c3 100644
--- a/attrib/igatttool.c
+++ b/attrib/igatttool.c
@@ -25,15 +25,24 @@
#include <stdio.h>
#include <glib.h>

+#include <bluetooth/sdp.h>
+
#include <readline/readline.h>
#include <readline/history.h>

+#include "btio.h"
+#include "gattrib.h"
#include "gatttool.h"

+static GIOChannel *iochannel = NULL;
+static GAttrib *attrib = NULL;
static GMainLoop *event_loop;
+static GString *prompt;

static void cmd_help(int argcp, char **argvp);
static void cmd_exit(int argcp, char **argvp);
+static void cmd_connect(int argcp, char **argvp);
+static void cmd_disconnect(int argcp, char **argvp);

static struct {
const char *cmd;
@@ -42,9 +51,62 @@ static struct {
} commands[] = {
{ "help", cmd_help, "Show this help"},
{ "exit", cmd_exit, "Exit interactive mode"},
+ { "connect", cmd_connect, "Connect to a remote device"},
+ { "disconnect", cmd_disconnect, "Disconnect from a remote device"},
{ NULL, NULL, NULL}
};

+enum state {
+ STATE_DISCONNECTED,
+ STATE_CONNECTING,
+ STATE_CONNECTED
+} conn_state;
+
+static char *get_prompt(void)
+{
+ if (conn_state == STATE_CONNECTING) {
+ g_string_assign(prompt, "Connecting... ");
+ return prompt->str;
+ }
+
+ if (conn_state == STATE_CONNECTED)
+ g_string_assign(prompt, "[CON]");
+ else
+ g_string_assign(prompt, "[ ]");
+
+ if (main_opts.dst)
+ g_string_append_printf(prompt, "[%17s]", main_opts.dst);
+ else
+ g_string_append_printf(prompt, "[%17s]", "");
+
+ if (main_opts.le)
+ g_string_append(prompt, "[LE]");
+ else
+ g_string_append(prompt, "[BR]");
+
+ g_string_append(prompt, "> ");
+
+ return prompt->str;
+}
+
+static void set_state(enum state st)
+{
+ conn_state = st;
+ rl_set_prompt(get_prompt());
+ rl_redisplay();
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+ if (err) {
+ printf("connect error: %s\n", err->message);
+ return;
+ }
+
+ attrib = g_attrib_new(iochannel);
+ set_state(STATE_CONNECTED);
+}
+
static void cmd_help(int argcp, char **argvp)
{
int i;
@@ -59,6 +121,41 @@ static void cmd_exit(int argcp, char **argvp)
g_main_loop_quit(event_loop);
}

+static void cmd_connect(int argcp, char **argvp)
+{
+ if (conn_state != STATE_DISCONNECTED)
+ return;
+
+ if (main_opts.dst == NULL) {
+ printf("Remote Bluetooth address required\n");
+ return;
+ }
+
+ set_state(STATE_CONNECTING);
+ iochannel = do_connect(main_opts.le, connect_cb);
+ if (iochannel == NULL)
+ set_state(STATE_DISCONNECTED);
+
+ return;
+}
+
+static void cmd_disconnect(int argcp, char **argvp)
+{
+ if (conn_state == STATE_DISCONNECTED)
+ return;
+
+ g_attrib_unref(attrib);
+ attrib = NULL;
+
+ g_io_channel_shutdown(iochannel, FALSE, NULL);
+ g_io_channel_unref(iochannel);
+ iochannel = NULL;
+
+ set_state(STATE_DISCONNECTED);
+
+ return;
+}
+
static void parse_line(char *line_read)
{
gchar **argvp;
@@ -110,6 +207,8 @@ int do_interactive(void)
GIOChannel *pchan;
gint events;

+ prompt = g_string_new(NULL);
+
event_loop = g_main_loop_new(NULL, FALSE);

pchan = g_io_channel_unix_new(fileno(stdin));
@@ -117,13 +216,15 @@ int do_interactive(void)
events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
g_io_add_watch(pchan, events, prompt_read, NULL);

- rl_callback_handler_install("> ", parse_line);
+ rl_callback_handler_install(get_prompt(), parse_line);

g_main_loop_run(event_loop);

rl_callback_handler_remove();
+ cmd_disconnect(0, NULL);
g_io_channel_unref(pchan);
g_main_loop_unref(event_loop);
+ g_string_free(prompt, TRUE);

return 0;
}
--
1.7.1


2011-02-08 15:32:15

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH 4/5] Move main_opts from gatttool.c to header file

---
attrib/gatttool.c | 21 ---------------------
attrib/gatttool.h | 21 +++++++++++++++++++++
2 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index 3eb3b0b..e0de05b 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -50,27 +50,6 @@
static GMainLoop *event_loop;
static gboolean got_error = FALSE;

-static struct main_opts {
- gchar *src;
- gchar *dst;
- gchar *value;
- gchar *sec_level;
- uuid_t *uuid;
- int start;
- int end;
- int handle;
- int mtu;
- int psm;
- gboolean primary;
- gboolean char_disc;
- gboolean char_read;
- gboolean listen;
- gboolean char_desc;
- gboolean le;
- gboolean char_write;
- gboolean interactive;
-} main_opts;
-
struct characteristic_data {
GAttrib *attrib;
uint16_t start;
diff --git a/attrib/gatttool.h b/attrib/gatttool.h
index 9190730..0f78aa8 100644
--- a/attrib/gatttool.h
+++ b/attrib/gatttool.h
@@ -21,4 +21,25 @@
*
*/

+struct main_opts {
+ gchar *src;
+ gchar *dst;
+ gchar *value;
+ gchar *sec_level;
+ uuid_t *uuid;
+ int start;
+ int end;
+ int handle;
+ int mtu;
+ int psm;
+ gboolean primary;
+ gboolean char_disc;
+ gboolean char_read;
+ gboolean listen;
+ gboolean char_desc;
+ gboolean le;
+ gboolean char_write;
+ gboolean interactive;
+} main_opts;
+
int do_interactive(void);
--
1.7.1


2011-02-08 15:32:14

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH 3/5] Add an initial interactive mode to gatttool

Mode required to allow better GATT procedures control. Some scenarios
require sequential commands without disconnection and delay between
operations. It is also desirable to change some connection parameters
of an active connection.
---
Makefile.am | 8 ++-
attrib/gatttool.c | 9 ++++
attrib/gatttool.h | 24 ++++++++++
attrib/igatttool.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++
configure.ac | 1 +
5 files changed, 169 insertions(+), 2 deletions(-)
create mode 100644 attrib/gatttool.h
create mode 100644 attrib/igatttool.c

diff --git a/Makefile.am b/Makefile.am
index e6639a7..ee3cd82 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -175,12 +175,16 @@ builtin_sources += plugins/service.c
endif

if ATTRIBPLUGIN
+
+if READLINE
bin_PROGRAMS += attrib/gatttool

attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
attrib/gattrib.c btio/btio.c \
- src/glib-helper.h src/glib-helper.c
-attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@
+ src/glib-helper.h src/glib-helper.c \
+ attrib/gatttool.h attrib/igatttool.c
+attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @READLINE_LIBS@
+endif

builtin_modules += attrib
builtin_sources += attrib/main.c \
diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index c612309..3eb3b0b 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -42,6 +42,7 @@
#include "gattrib.h"
#include "glib-helper.h"
#include "gatt.h"
+#include "gatttool.h"

/* Minimum MTU for L2CAP connections over BR/EDR */
#define ATT_MIN_MTU_L2CAP 48
@@ -67,6 +68,7 @@ static struct main_opts {
gboolean char_desc;
gboolean le;
gboolean char_write;
+ gboolean interactive;
} main_opts;

struct characteristic_data {
@@ -565,6 +567,8 @@ static GOptionEntry gatt_options[] = {
"Listen for notifications and indications", NULL },
{ "le", 0, 0, G_OPTION_ARG_NONE, &main_opts.le,
"Use Bluetooth Low Energy transport", NULL },
+ { "interactive", 'I', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE,
+ &main_opts.interactive, "Use interactive mode", NULL },
{ NULL },
};

@@ -624,6 +628,11 @@ int main(int argc, char *argv[])
g_error_free(gerr);
}

+ if (main_opts.interactive) {
+ do_interactive();
+ goto done;
+ }
+
if (main_opts.primary)
callback = primary;
else if (main_opts.char_disc)
diff --git a/attrib/gatttool.h b/attrib/gatttool.h
new file mode 100644
index 0000000..9190730
--- /dev/null
+++ b/attrib/gatttool.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int do_interactive(void);
diff --git a/attrib/igatttool.c b/attrib/igatttool.c
new file mode 100644
index 0000000..cac97f0
--- /dev/null
+++ b/attrib/igatttool.c
@@ -0,0 +1,129 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "gatttool.h"
+
+static GMainLoop *event_loop;
+
+static void cmd_help(int argcp, char **argvp);
+static void cmd_exit(int argcp, char **argvp);
+
+static struct {
+ const char *cmd;
+ void (*func)(int argcp, char **argvp);
+ const char *desc;
+} commands[] = {
+ { "help", cmd_help, "Show this help"},
+ { "exit", cmd_exit, "Exit interactive mode"},
+ { NULL, NULL, NULL}
+};
+
+static void cmd_help(int argcp, char **argvp)
+{
+ int i;
+
+ for (i = 0; commands[i].cmd; i++)
+ printf("%-12s\t%s\n", commands[i].cmd, commands[i].desc);
+}
+
+static void cmd_exit(int argcp, char **argvp)
+{
+ rl_callback_handler_remove();
+ g_main_loop_quit(event_loop);
+}
+
+static void parse_line(char *line_read)
+{
+ gchar **argvp;
+ int argcp;
+ int i;
+
+ if (line_read == NULL) {
+ printf("\n");
+ cmd_exit(0, NULL);
+ return;
+ }
+
+ line_read = g_strstrip(line_read);
+
+ if (*line_read == '\0')
+ return;
+
+ add_history(line_read);
+
+ g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
+
+ for (i = 0; commands[i].cmd; i++)
+ if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
+ break;
+
+ if (commands[i].cmd)
+ commands[i].func(argcp, argvp);
+ else
+ printf("%s: command not found\n", argvp[0]);
+
+ g_strfreev(argvp);
+}
+
+static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ g_io_channel_unref(chan);
+ return FALSE;
+ }
+
+ rl_callback_read_char();
+
+ return TRUE;
+}
+
+int do_interactive(void)
+{
+ GIOChannel *pchan;
+ gint events;
+
+ event_loop = g_main_loop_new(NULL, FALSE);
+
+ pchan = g_io_channel_unix_new(fileno(stdin));
+ g_io_channel_set_close_on_unref(pchan, TRUE);
+ events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ g_io_add_watch(pchan, events, prompt_read, NULL);
+
+ rl_callback_handler_install("> ", parse_line);
+
+ g_main_loop_run(event_loop);
+
+ rl_callback_handler_remove();
+ g_io_channel_unref(pchan);
+ g_main_loop_unref(event_loop);
+
+ return 0;
+}
diff --git a/configure.ac b/configure.ac
index bebdc9c..a8e8fc6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,7 @@ AC_PATH_GSTREAMER
AC_PATH_USB
AC_PATH_SNDFILE
AC_PATH_OUI
+AC_PATH_READLINE

AC_ARG_BLUEZ

--
1.7.1


2011-02-08 15:32:13

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH 2/5] Include check to readline lib on acinlude.m4

---
acinclude.m4 | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/acinclude.m4 b/acinclude.m4
index 9d3f6b2..91e0956 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -153,6 +153,15 @@ AC_DEFUN([AC_PATH_SNDFILE], [
AC_SUBST(SNDFILE_LIBS)
])

+AC_DEFUN([AC_PATH_READLINE], [
+ AC_CHECK_HEADER(readline/readline.h,
+ AC_CHECK_LIB(readline, main,
+ [ readline_found=yes
+ AC_SUBST(READLINE_LIBS, "-lreadline")
+ ], readline_found=no),
+ [])
+])
+
AC_DEFUN([AC_PATH_OUI], [
AC_ARG_WITH(ouifile,
AS_HELP_STRING([--with-ouifile=PATH],[Path to the oui.txt file @<:@auto@:>@]),
@@ -356,6 +365,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
AM_CONDITIONAL(HEALTHPLUGIN, test "${health_enable}" = "yes")
AM_CONDITIONAL(MCAP, test "${health_enable}" = "yes")
AM_CONDITIONAL(HAL, test "${hal_enable}" = "yes")
+ AM_CONDITIONAL(READLINE, test "${readline_found}" = "yes")
AM_CONDITIONAL(ATTRIBPLUGIN, test "${attrib_enable}" = "yes")
AM_CONDITIONAL(ECHOPLUGIN, test "no" = "yes")
AM_CONDITIONAL(PNATPLUGIN, test "${pnat_enable}" = "yes")
--
1.7.1