2011-01-21 13:46:20

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH 1/7] Create a file to hold the generic code from gatttool

We need a interactive tool that should be similar to gatttool in
features. So we can share some code between them.
---
Makefile.am | 3 +-
attrib/gatttool.c | 85 +----------------------------------
attrib/gtcommon.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++
attrib/gtcommon.h | 45 ++++++++++++++++++
4 files changed, 178 insertions(+), 85 deletions(-)
create mode 100644 attrib/gtcommon.c
create mode 100644 attrib/gtcommon.h

diff --git a/Makefile.am b/Makefile.am
index 5f96975..5274435 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -179,7 +179,8 @@ 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
+ src/glib-helper.h src/glib-helper.c \
+ attrib/gtcommon.h attrib/gtcommon.c
attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@

builtin_modules += attrib
diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index 8e8ed8e..e5ebad1 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -42,27 +42,11 @@
#include "gattrib.h"
#include "glib-helper.h"
#include "gatt.h"
+#include "gtcommon.h"

/* 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;

@@ -487,73 +471,6 @@ static gboolean characteristics_desc(gpointer user_data)
return FALSE;
}

-static gboolean parse_uuid(const char *key, const char *value,
- gpointer user_data, GError **error)
-{
- if (!value)
- return FALSE;
-
- opt_uuid = g_try_malloc(sizeof(uuid_t));
- if (opt_uuid == NULL)
- return FALSE;
-
- if (bt_string2uuid(opt_uuid, value) < 0)
- return FALSE;
-
- return TRUE;
-}
-
-static GOptionEntry primary_char_options[] = {
- { "start", 's' , 0, G_OPTION_ARG_INT, &opt_start,
- "Starting handle(optional)", "0x0001" },
- { "end", 'e' , 0, G_OPTION_ARG_INT, &opt_end,
- "Ending handle(optional)", "0xffff" },
- { "uuid", 'u', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
- parse_uuid, "UUID16 or UUID128(optional)", "0x1801"},
- { NULL },
-};
-
-static GOptionEntry char_rw_options[] = {
- { "handle", 'a' , 0, G_OPTION_ARG_INT, &opt_handle,
- "Read/Write characteristic by handle(required)", "0x0001" },
- { "value", 'n' , 0, G_OPTION_ARG_STRING, &opt_value,
- "Write characteristic value (required for write operation)",
- "0x0001" },
- {NULL},
-};
-
-static GOptionEntry gatt_options[] = {
- { "primary", 0, 0, G_OPTION_ARG_NONE, &opt_primary,
- "Primary Service Discovery", NULL },
- { "characteristics", 0, 0, G_OPTION_ARG_NONE, &opt_characteristics,
- "Characteristics Discovery", NULL },
- { "char-read", 0, 0, G_OPTION_ARG_NONE, &opt_char_read,
- "Characteristics Value/Descriptor Read", NULL },
- { "char-write", 0, 0, G_OPTION_ARG_NONE, &opt_char_write,
- "Characteristics Value Write", NULL },
- { "char-desc", 0, 0, G_OPTION_ARG_NONE, &opt_char_desc,
- "Characteristics Descriptor Discovery", NULL },
- { "listen", 0, 0, G_OPTION_ARG_NONE, &opt_listen,
- "Listen for notifications and indications", NULL },
- { "le", 0, 0, G_OPTION_ARG_NONE, &opt_le,
- "Use Bluetooth Low Energy transport", NULL },
- { NULL },
-};
-
-static GOptionEntry options[] = {
- { "adapter", 'i', 0, G_OPTION_ARG_STRING, &opt_src,
- "Specify local adapter interface", "hciX" },
- { "device", 'b', 0, G_OPTION_ARG_STRING, &opt_dst,
- "Specify remote Bluetooth address", "MAC" },
- { "mtu", 'm', 0, G_OPTION_ARG_INT, &opt_mtu,
- "Specify the MTU size", "MTU" },
- { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
- "Specify the PSM for GATT/ATT over BR/EDR", "PSM" },
- { "sec-level", 'l', 0, G_OPTION_ARG_STRING, &opt_sec_level,
- "Set security level. Default: low", "[low | medium | high]"},
- { NULL },
-};
-
int main(int argc, char *argv[])
{
GOptionContext *context;
diff --git a/attrib/gtcommon.c b/attrib/gtcommon.c
new file mode 100644
index 0000000..ebf47e9
--- /dev/null
+++ b/attrib/gtcommon.c
@@ -0,0 +1,130 @@
+/*
+ *
+ * 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 <errno.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "att.h"
+#include "btio.h"
+#include "gattrib.h"
+#include "glib-helper.h"
+#include "gatt.h"
+
+#include "gtcommon.h"
+
+gchar *opt_src = NULL;
+gchar *opt_dst = NULL;
+gchar *opt_value = NULL;
+gchar *opt_sec_level = "low";
+uuid_t *opt_uuid;
+int opt_start = 0x0001;
+int opt_end = 0xffff;
+int opt_handle = -1;
+int opt_mtu = 0;
+int opt_psm = 0x1f;
+gboolean opt_primary = FALSE;
+gboolean opt_characteristics = FALSE;
+gboolean opt_char_read = FALSE;
+gboolean opt_listen = FALSE;
+gboolean opt_char_desc = FALSE;
+gboolean opt_le = FALSE;
+gboolean opt_char_write = FALSE;
+
+static gboolean parse_uuid(const char *key, const char *value,
+ gpointer user_data, GError **gerr)
+{
+ if (!value)
+ return FALSE;
+
+ opt_uuid = g_try_malloc(sizeof(uuid_t));
+ if (opt_uuid == NULL)
+ return FALSE;
+
+ if (bt_string2uuid(opt_uuid, value) < 0) {
+ g_free(opt_uuid);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+GOptionEntry primary_char_options[] = {
+ { "start", 's' , 0, G_OPTION_ARG_INT, &opt_start,
+ "Starting handle(optional)", "0x0001" },
+ { "end", 'e' , 0, G_OPTION_ARG_INT, &opt_end,
+ "Ending handle(optional)", "0xffff" },
+ { "uuid", 'u', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ parse_uuid, "UUID16 or UUID128(optional)", "0x1801"},
+ { NULL },
+};
+
+GOptionEntry char_rw_options[] = {
+ { "handle", 'a' , 0, G_OPTION_ARG_INT, &opt_handle,
+ "Read/Write characteristic by handle(required)", "0x0001" },
+ { "value", 'n' , 0, G_OPTION_ARG_STRING, &opt_value,
+ "Write characteristic value (required for write operation)",
+ "0x0001" },
+ {NULL},
+};
+
+GOptionEntry gatt_options[] = {
+ { "primary", 0, 0, G_OPTION_ARG_NONE, &opt_primary,
+ "Primary Service Discovery", NULL },
+ { "characteristics", 0, 0, G_OPTION_ARG_NONE, &opt_characteristics,
+ "Characteristics Discovery", NULL },
+ { "char-read", 0, 0, G_OPTION_ARG_NONE, &opt_char_read,
+ "Characteristics Value/Descriptor Read", NULL },
+ { "char-write", 0, 0, G_OPTION_ARG_NONE, &opt_char_write,
+ "Characteristics Value Write", NULL },
+ { "char-desc", 0, 0, G_OPTION_ARG_NONE, &opt_char_desc,
+ "Characteristics Descriptor Discovery", NULL },
+ { "listen", 0, 0, G_OPTION_ARG_NONE, &opt_listen,
+ "Listen for notifications and indications", NULL },
+ { "le", 0, 0, G_OPTION_ARG_NONE, &opt_le,
+ "Use Bluetooth Low Energy transport", NULL },
+ { NULL },
+};
+
+GOptionEntry options[] = {
+ { "adapter", 'i', 0, G_OPTION_ARG_STRING, &opt_src,
+ "Specify local adapter interface", "hciX" },
+ { "device", 'b', 0, G_OPTION_ARG_STRING, &opt_dst,
+ "Specify remote Bluetooth address", "MAC" },
+ { "mtu", 'm', 0, G_OPTION_ARG_INT, &opt_mtu,
+ "Specify the MTU size", "MTU" },
+ { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
+ "Specify the PSM for GATT/ATT over BR/EDR", "PSM" },
+ { "sec-level", 'l', 0, G_OPTION_ARG_STRING, &opt_sec_level,
+ "Set security level. Default: low", "[low | medium | high]"},
+ { NULL },
+};
+
diff --git a/attrib/gtcommon.h b/attrib/gtcommon.h
new file mode 100644
index 0000000..30e8e37
--- /dev/null
+++ b/attrib/gtcommon.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * 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
+ *
+ */
+
+extern gchar *opt_src;
+extern gchar *opt_dst;
+extern gchar *opt_value;
+extern gchar *opt_sec_level;
+extern uuid_t *opt_uuid;
+extern int opt_start;
+extern int opt_end;
+extern int opt_handle;
+extern int opt_mtu;
+extern int opt_psm;
+extern gboolean opt_primary;
+extern gboolean opt_characteristics;
+extern gboolean opt_char_read;
+extern gboolean opt_listen;
+extern gboolean opt_char_desc;
+extern gboolean opt_le;
+extern gboolean opt_char_write;
+
+extern GOptionEntry primary_char_options[];
+extern GOptionEntry char_rw_options[];
+extern GOptionEntry gatt_options[];
+extern GOptionEntry options[];
--
1.7.1



2011-01-21 17:50:56

by Anderson Lizardo

[permalink] [raw]
Subject: Re: [PATCH 5/7] Add transport option to interactive gatttool

On Fri, Jan 21, 2011 at 9:46 AM, Sheldon Demario
<[email protected]> wrote:
> ---
> ?attrib/igatttool.c | ? 18 ++++++++++++++++++
> ?1 files changed, 18 insertions(+), 0 deletions(-)
>
> diff --git a/attrib/igatttool.c b/attrib/igatttool.c
> index 29290ed..e0d8b44 100644
> --- a/attrib/igatttool.c
> +++ b/attrib/igatttool.c
> @@ -137,6 +137,23 @@ static void cmd_exit(char **cmd)
> ? ? ? ?g_main_loop_quit(main_loop);
> ?}
>
> +static void cmd_transport(char **cmd)

You can call this parameter "opt" for consistency with other commands.

> +{
> + ? ? ? if (cmd[1] == NULL)
> + ? ? ? ? ? ? ? return;
> +
> + ? ? ? if (conn_state != STATE_DISCONNECTED)
> + ? ? ? ? ? ? ? return;
> +
> + ? ? ? if (strcasecmp(cmd[1], "le") == 0)
> + ? ? ? ? ? ? ? opt_le = TRUE;
> + ? ? ? else if (strcasecmp(cmd[1], "br") == 0)
> + ? ? ? ? ? ? ? opt_le = FALSE;

I've noticed every command callback has the two lines below. Would be
possible to move them to after the callback is called ?

> +
> + ? ? ? rl_set_prompt(get_prompt());
> + ? ? ? rl_redisplay();
> +}
> +

Regards,
--
Anderson Lizardo
OpenBossa Labs - INdT
Manaus - Brazil

2011-01-21 13:46:25

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH 6/7] Add autoconf macro for interactive gatttool

From: Claudio Takahasi <[email protected]>

Interactive gatttool will be built automatically if readline library
is found and attribute plugin is enabled.
---
Makefile.am | 8 ++++++--
acinclude.m4 | 8 ++++++++
configure.ac | 1 +
3 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 79365ad..f9e05fd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -175,7 +175,7 @@ builtin_sources += plugins/service.c
endif

if ATTRIBPLUGIN
-bin_PROGRAMS += attrib/gatttool attrib/igatttool
+bin_PROGRAMS += attrib/gatttool

attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
attrib/gattrib.c btio/btio.c \
@@ -183,10 +183,14 @@ attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
attrib/gtcommon.h attrib/gtcommon.c
attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@

+if IGATTTOOL
+bin_PROGRAMS += attrib/igatttool
+
attrib_igatttool_SOURCES = attrib/igatttool.c btio/btio.c \
attrib/gtcommon.h attrib/gtcommon.c \
src/glib-helper.h src/glib-helper.c
-attrib_igatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@ -lreadline
+attrib_igatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @READLINE_LIBS@
+endif

builtin_modules += attrib
builtin_sources += attrib/main.c \
diff --git a/acinclude.m4 b/acinclude.m4
index ecf4b4b..2b4b515 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -153,6 +153,13 @@ AC_DEFUN([AC_PATH_SNDFILE], [
AC_SUBST(SNDFILE_LIBS)
])

+AC_DEFUN([AC_PATH_READLINE], [
+ 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@:>@]),
@@ -357,6 +364,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
AM_CONDITIONAL(MCAP, test "${health_enable}" = "yes")
AM_CONDITIONAL(HAL, test "${hal_enable}" = "yes")
AM_CONDITIONAL(ATTRIBPLUGIN, test "${attrib_enable}" = "yes")
+ AM_CONDITIONAL(IGATTTOOL, test "${attrib_enable}" = "yes" && test "${readline_found}" = "yes")
AM_CONDITIONAL(ECHOPLUGIN, test "no" = "yes")
AM_CONDITIONAL(PNATPLUGIN, test "${pnat_enable}" = "yes")
AM_CONDITIONAL(TRACER, test "${tracer_enable}" = "yes")
diff --git a/configure.ac b/configure.ac
index 4a979f0..6925344 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,6 +41,7 @@ AC_PATH_ALSA
AC_PATH_GSTREAMER
AC_PATH_USB
AC_PATH_SNDFILE
+AC_PATH_READLINE
AC_PATH_OUI

AC_ARG_BLUEZ
--
1.7.1


2011-01-21 13:46:26

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH 7/7] Initial primary service discovery in igatttool

---
Makefile.am | 3 ++-
attrib/igatttool.c | 44 +++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index f9e05fd..ef9afb2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -188,7 +188,8 @@ bin_PROGRAMS += attrib/igatttool

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

diff --git a/attrib/igatttool.c b/attrib/igatttool.c
index e0d8b44..d3084d6 100644
--- a/attrib/igatttool.c
+++ b/attrib/igatttool.c
@@ -30,8 +30,12 @@
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>

+#include "gattrib.h"
+#include "gatt.h"
#include "btio.h"
+#include "att.h"

#include "gtcommon.h"

@@ -39,6 +43,7 @@ GIOChannel *iochannel = NULL;
GMainLoop *main_loop = NULL;
gboolean status = FALSE;
GString *prompt = NULL;
+GAttrib *attrib = NULL;

enum state {
STATE_DISCONNECTED,
@@ -96,9 +101,32 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
return;
}

+ attrib = g_attrib_new(iochannel);
set_state(STATE_CONNECTED);
}

+static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
+{
+ GSList *l;
+
+ if (status) {
+ show_message("Discover all primary services failed: %s\n",
+ att_ecode2str(status));
+ return;
+ }
+
+ rl_save_prompt();
+ for (l = services; l; l = l->next) {
+ struct att_primary *prim = l->data;
+ rl_message("attr handle = 0x%04x, end grp handle = 0x%04x "
+ "uuid: %s\n", prim->start, prim->end, prim->uuid);
+ rl_on_new_line();
+ }
+
+ rl_restore_prompt();
+ rl_forced_update_display();
+}
+
static void cmd_connect(char **cmd)
{
if (conn_state != STATE_DISCONNECTED)
@@ -125,6 +153,9 @@ static void cmd_disconnect(char **cmd)
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;
@@ -166,6 +197,16 @@ static void cmd_psm(char **opt)
rl_redisplay();
}

+static void cmd_primary(char **cmd)
+{
+ if (conn_state != STATE_CONNECTED) {
+ show_message("Fail: disconnected\n");
+ return;
+ }
+
+ gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);
+}
+
static struct {
char *cmd;
void (*func)(char **cmd);
@@ -177,6 +218,7 @@ static struct {
{ "exit", cmd_exit, NULL, "Exit"},
{ "transport", cmd_transport, "<LE | BR>", "Set transport"},
{ "psm", cmd_psm, "<psm>", "Set psm"},
+ { "primary", cmd_primary, NULL, "Primary Service Discovery"},
{ NULL, NULL, NULL, NULL}
};

@@ -234,7 +276,6 @@ int main(int argc, char *argv[])
}

main_loop = g_main_loop_new(NULL, FALSE);
-
prompt = g_string_new(NULL);

pchan = g_io_channel_unix_new(fileno(stdin));
@@ -247,6 +288,7 @@ int main(int argc, char *argv[])
g_main_loop_run(main_loop);

cmd_disconnect(NULL);
+ g_attrib_unref(attrib);
rl_callback_handler_remove();
g_io_channel_unref(pchan);
g_main_loop_unref(main_loop);
--
1.7.1


2011-01-21 13:46:24

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH 5/7] Add transport option to interactive gatttool

---
attrib/igatttool.c | 18 ++++++++++++++++++
1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/attrib/igatttool.c b/attrib/igatttool.c
index 29290ed..e0d8b44 100644
--- a/attrib/igatttool.c
+++ b/attrib/igatttool.c
@@ -137,6 +137,23 @@ static void cmd_exit(char **cmd)
g_main_loop_quit(main_loop);
}

+static void cmd_transport(char **cmd)
+{
+ if (cmd[1] == NULL)
+ return;
+
+ if (conn_state != STATE_DISCONNECTED)
+ return;
+
+ if (strcasecmp(cmd[1], "le") == 0)
+ opt_le = TRUE;
+ else if (strcasecmp(cmd[1], "br") == 0)
+ opt_le = FALSE;
+
+ rl_set_prompt(get_prompt());
+ rl_redisplay();
+}
+
static void cmd_psm(char **opt)
{
if (opt[1] == NULL)
@@ -158,6 +175,7 @@ static struct {
{ "connect", cmd_connect, "<bdaddr>", "Connect"},
{ "disconnect", cmd_disconnect, NULL, "Disconnect"},
{ "exit", cmd_exit, NULL, "Exit"},
+ { "transport", cmd_transport, "<LE | BR>", "Set transport"},
{ "psm", cmd_psm, "<psm>", "Set psm"},
{ NULL, NULL, NULL, NULL}
};
--
1.7.1


2011-01-21 13:46:23

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH 4/7] Add psm option to interactive gatttool

---
attrib/igatttool.c | 13 +++++++++++++
1 files changed, 13 insertions(+), 0 deletions(-)

diff --git a/attrib/igatttool.c b/attrib/igatttool.c
index 9cd2a1e..29290ed 100644
--- a/attrib/igatttool.c
+++ b/attrib/igatttool.c
@@ -137,6 +137,18 @@ static void cmd_exit(char **cmd)
g_main_loop_quit(main_loop);
}

+static void cmd_psm(char **opt)
+{
+ if (opt[1] == NULL)
+ return;
+
+ if (conn_state == STATE_DISCONNECTED || opt[1] != NULL)
+ opt_psm = atoi(opt[1]);
+
+ rl_set_prompt(get_prompt());
+ rl_redisplay();
+}
+
static struct {
char *cmd;
void (*func)(char **cmd);
@@ -146,6 +158,7 @@ static struct {
{ "connect", cmd_connect, "<bdaddr>", "Connect"},
{ "disconnect", cmd_disconnect, NULL, "Disconnect"},
{ "exit", cmd_exit, NULL, "Exit"},
+ { "psm", cmd_psm, "<psm>", "Set psm"},
{ NULL, NULL, NULL, NULL}
};

--
1.7.1


2011-01-21 13:46:22

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH 3/7] Initial version of igatttool - a interactive gatttool

Due the qualification tests, we need a deeper control over the
gatttool's steps.
---
Makefile.am | 7 ++-
attrib/igatttool.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 231 insertions(+), 1 deletions(-)
create mode 100644 attrib/igatttool.c

diff --git a/Makefile.am b/Makefile.am
index 5274435..79365ad 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -175,7 +175,7 @@ builtin_sources += plugins/service.c
endif

if ATTRIBPLUGIN
-bin_PROGRAMS += attrib/gatttool
+bin_PROGRAMS += attrib/gatttool attrib/igatttool

attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
attrib/gattrib.c btio/btio.c \
@@ -183,6 +183,11 @@ attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
attrib/gtcommon.h attrib/gtcommon.c
attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@

+attrib_igatttool_SOURCES = attrib/igatttool.c btio/btio.c \
+ attrib/gtcommon.h attrib/gtcommon.c \
+ src/glib-helper.h src/glib-helper.c
+attrib_igatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@ -lreadline
+
builtin_modules += attrib
builtin_sources += attrib/main.c \
attrib/manager.h attrib/manager.c \
diff --git a/attrib/igatttool.c b/attrib/igatttool.c
new file mode 100644
index 0000000..9cd2a1e
--- /dev/null
+++ b/attrib/igatttool.c
@@ -0,0 +1,225 @@
+/*
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+
+#include "btio.h"
+
+#include "gtcommon.h"
+
+GIOChannel *iochannel = NULL;
+GMainLoop *main_loop = NULL;
+gboolean status = FALSE;
+GString *prompt = 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 (opt_le)
+ g_string_append(prompt, "[LE]");
+ else
+ g_string_append(prompt, "[BR]");
+
+ g_string_append(prompt, "> ");
+
+ return prompt->str;
+}
+
+static void show_message(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ vprintf(format, ap);
+
+ va_end(ap);
+
+ rl_redisplay();
+}
+
+static void set_state(enum state st)
+{
+ conn_state = st;
+ rl_set_prompt(get_prompt());
+ rl_redisplay();
+}
+
+static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+ if (gerr) {
+ show_message("connect error: %s\n", gerr->message);
+ set_state(STATE_DISCONNECTED);
+ return;
+ }
+
+ set_state(STATE_CONNECTED);
+}
+
+static void cmd_connect(char **cmd)
+{
+ if (conn_state != STATE_DISCONNECTED)
+ return;
+
+ if (cmd[1] != NULL) {
+ g_free(opt_dst);
+ opt_dst = strdup(cmd[1]);
+ }
+
+ if (opt_dst == NULL) {
+ show_message("Remote Bluetooth address required\n");
+ return;
+ }
+
+ set_state(STATE_CONNECTING);
+ iochannel = do_connect(opt_le, opt_dst, connect_cb);
+ if (iochannel == NULL)
+ set_state(STATE_DISCONNECTED);
+}
+
+static void cmd_disconnect(char **cmd)
+{
+ if (conn_state == STATE_DISCONNECTED)
+ return;
+
+ g_io_channel_shutdown(iochannel, FALSE, NULL);
+ g_io_channel_unref(iochannel);
+ iochannel = NULL;
+
+ set_state(STATE_DISCONNECTED);
+}
+
+static void cmd_exit(char **cmd)
+{
+ g_main_loop_quit(main_loop);
+}
+
+static struct {
+ char *cmd;
+ void (*func)(char **cmd);
+ char *param;
+ char *desc;
+} commands[] = {
+ { "connect", cmd_connect, "<bdaddr>", "Connect"},
+ { "disconnect", cmd_disconnect, NULL, "Disconnect"},
+ { "exit", cmd_exit, NULL, "Exit"},
+ { NULL, NULL, NULL, NULL}
+};
+
+static void parse_line(char *line_read)
+{
+ char **command;
+ int j;
+
+ if (!(line_read && *line_read))
+ return;
+
+ add_history(line_read);
+
+ line_read = g_strstrip(line_read);
+
+ command = g_strsplit(line_read, " ", -1);
+ for (j = 0; commands[j].cmd; j++) {
+ if (strcasecmp(commands[j].cmd, command[0]))
+ continue;
+
+ commands[j].func(command);
+ }
+
+ g_strfreev(command);
+}
+
+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;
+ }
+
+ if (conn_state != STATE_CONNECTING)
+ rl_callback_read_char();
+
+ return TRUE;
+}
+
+int main(int argc, char *argv[])
+{
+ GOptionContext *context;
+ GIOChannel *pchan;
+ GError *gerr;
+ gint events;
+
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, options, NULL);
+
+ if (g_option_context_parse(context, &argc, &argv, &gerr) == FALSE) {
+ g_printerr("%s\n", gerr->message);
+ g_error_free(gerr);
+ exit(1);
+ }
+
+ main_loop = g_main_loop_new(NULL, FALSE);
+
+ prompt = g_string_new(NULL);
+
+ 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(get_prompt(), parse_line);
+
+ g_main_loop_run(main_loop);
+
+ cmd_disconnect(NULL);
+ rl_callback_handler_remove();
+ g_io_channel_unref(pchan);
+ g_main_loop_unref(main_loop);
+ g_option_context_free(context);
+
+ return 0;
+}
--
1.7.1


2011-01-21 13:46:21

by Sheldon Demario

[permalink] [raw]
Subject: [PATCH 2/7] Move do_connect from gatttool to gtcommon

---
attrib/gatttool.c | 70 +----------------------------------------------------
attrib/gtcommon.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++
attrib/gtcommon.h | 5 ++++
3 files changed, 69 insertions(+), 69 deletions(-)

diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index e5ebad1..e87ff0c 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -40,13 +40,9 @@
#include "att.h"
#include "btio.h"
#include "gattrib.h"
-#include "glib-helper.h"
#include "gatt.h"
#include "gtcommon.h"

-/* Minimum MTU for L2CAP connections over BR/EDR */
-#define ATT_MIN_MTU_L2CAP 48
-
static GMainLoop *event_loop;
static gboolean got_error = FALSE;

@@ -65,70 +61,6 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
}
}

-static GIOChannel *do_connect(gboolean le)
-{
- GIOChannel *chan;
- bdaddr_t sba, dba;
- GError *err = NULL;
- BtIOSecLevel sec_level;
-
- /* 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) {
- g_printerr("MTU cannot be smaller than %d\n",
- ATT_MIN_MTU_L2CAP);
- return NULL;
- }
-
- /* Remote device */
- if (opt_dst == NULL) {
- g_printerr("Remote Bluetooth address required\n");
- return NULL;
- }
- str2ba(opt_dst, &dba);
-
- /* Local adapter */
- if (opt_src != NULL) {
- if (!strncmp(opt_src, "hci", 3))
- hci_devba(atoi(opt_src + 3), &sba);
- else
- str2ba(opt_src, &sba);
- } else
- bacpy(&sba, BDADDR_ANY);
-
- if (strcmp(opt_sec_level, "medium") == 0)
- sec_level = BT_IO_SEC_MEDIUM;
- else if (strcmp(opt_sec_level, "high") == 0)
- sec_level = BT_IO_SEC_HIGH;
- else
- sec_level = BT_IO_SEC_LOW;
-
- if (le)
- 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_CID, GATT_CID,
- BT_IO_OPT_OMTU, opt_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_SEC_LEVEL, sec_level,
- BT_IO_OPT_INVALID);
-
- if (err) {
- g_printerr("%s\n", err->message);
- g_error_free(err);
- return NULL;
- }
-
- return chan;
-}
-
static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
{
GSList *l;
@@ -529,7 +461,7 @@ int main(int argc, char *argv[])
goto done;
}

- chan = do_connect(opt_le);
+ chan = do_connect(opt_le, opt_dst, connect_cb);
if (chan == NULL) {
got_error = TRUE;
goto done;
diff --git a/attrib/gtcommon.c b/attrib/gtcommon.c
index ebf47e9..0759990 100644
--- a/attrib/gtcommon.c
+++ b/attrib/gtcommon.c
@@ -128,3 +128,66 @@ GOptionEntry options[] = {
{ NULL },
};

+GIOChannel *do_connect(gboolean le, gchar *dest, BtIOConnect connect_cb)
+{
+ GIOChannel *chan;
+ bdaddr_t sba, dba;
+ GError *err = NULL;
+ BtIOSecLevel sec_level;
+
+ /* 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) {
+ g_printerr("MTU cannot be smaller than %d\n",
+ ATT_MIN_MTU_L2CAP);
+ return NULL;
+ }
+
+ /* Remote device */
+ if (dest == NULL) {
+ g_printerr("Remote Bluetooth address required\n");
+ return NULL;
+ }
+ str2ba(dest, &dba);
+
+ /* Local adapter */
+ if (opt_src != NULL) {
+ if (!strncmp(opt_src, "hci", 3))
+ hci_devba(atoi(opt_src + 3), &sba);
+ else
+ str2ba(opt_src, &sba);
+ } else
+ bacpy(&sba, BDADDR_ANY);
+
+ if (strcmp(opt_sec_level, "medium") == 0)
+ sec_level = BT_IO_SEC_MEDIUM;
+ else if (strcmp(opt_sec_level, "high") == 0)
+ sec_level = BT_IO_SEC_HIGH;
+ else
+ sec_level = BT_IO_SEC_LOW;
+
+ if (le)
+ 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_CID, GATT_CID,
+ BT_IO_OPT_OMTU, opt_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_SEC_LEVEL, sec_level,
+ BT_IO_OPT_INVALID);
+
+ if (err) {
+ g_printerr("%s\n", err->message);
+ g_error_free(err);
+ return NULL;
+ }
+
+ return chan;
+}
diff --git a/attrib/gtcommon.h b/attrib/gtcommon.h
index 30e8e37..ead2f5b 100644
--- a/attrib/gtcommon.h
+++ b/attrib/gtcommon.h
@@ -21,6 +21,9 @@
*
*/

+/* Minimum MTU for L2CAP connections over BR/EDR */
+#define ATT_MIN_MTU_L2CAP 48
+
extern gchar *opt_src;
extern gchar *opt_dst;
extern gchar *opt_value;
@@ -39,6 +42,8 @@ extern gboolean opt_char_desc;
extern gboolean opt_le;
extern gboolean opt_char_write;

+GIOChannel *do_connect(gboolean le, gchar *dest, BtIOConnect connect_db);
+
extern GOptionEntry primary_char_options[];
extern GOptionEntry char_rw_options[];
extern GOptionEntry gatt_options[];
--
1.7.1