Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH v3 01/20] cyclingspeed: Add CSC profile plugin skeleton
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

This patch adds stub profile driver plugin for CSC profile.
---
 Makefile.am                          |   5 +-
 lib/uuid.h                           |   2 +
 profiles/cyclingspeed/cyclingspeed.c | 192 +++++++++++++++++++++++++++++++++++
 3 files changed, 197 insertions(+), 2 deletions(-)
 create mode 100644 profiles/cyclingspeed/cyclingspeed.c

diff --git a/Makefile.am b/Makefile.am
index e95ba1a..d009828 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -178,7 +178,7 @@ builtin_sources += profiles/health/hdp_main.c profiles/health/hdp_types.h \
 endif
 
 builtin_modules += thermometer alert time gatt_example proximity deviceinfo \
-			gatt scanparam heartrate
+			gatt scanparam heartrate cyclingspeed
 builtin_sources += profiles/thermometer/main.c \
 			profiles/thermometer/manager.h \
 			profiles/thermometer/manager.c \
@@ -217,7 +217,8 @@ builtin_sources += profiles/thermometer/main.c \
 			profiles/heartrate/manager.c \
 			profiles/heartrate/manager.h \
 			profiles/heartrate/heartrate.c \
-			profiles/heartrate/heartrate.h
+			profiles/heartrate/heartrate.h \
+			profiles/cyclingspeed/cyclingspeed.c
 
 
 builtin_modules += formfactor
diff --git a/lib/uuid.h b/lib/uuid.h
index f85abbe..a9a3b13 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -74,6 +74,8 @@ extern "C" {
 #define INTERMEDIATE_TEMPERATURE_UUID	"00002a1e-0000-1000-8000-00805f9b34fb"
 #define MEASUREMENT_INTERVAL_UUID	"00002a21-0000-1000-8000-00805f9b34fb"
 
+#define CYCLING_SC_UUID		"00001816-0000-1000-8000-00805f9b34fb"
+
 #define RFCOMM_UUID_STR		"00000003-0000-1000-8000-00805f9b34fb"
 
 #define HDP_UUID		"00001400-0000-1000-8000-00805f9b34fb"
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
new file mode 100644
index 0000000..025b09a
--- /dev/null
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -0,0 +1,192 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Tieto Poland
+ *
+ *  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <bluetooth/uuid.h>
+
+#include "plugin.h"
+#include "adapter.h"
+#include "device.h"
+#include "profile.h"
+
+struct csc_adapter {
+	struct btd_adapter	*adapter;
+	GSList			*devices;	/* list of registered devices */
+};
+
+struct csc {
+	struct btd_device	*dev;
+	struct csc_adapter	*cadapter;
+};
+
+static GSList *csc_adapters = NULL;
+
+static gint cmp_adapter(gconstpointer a, gconstpointer b)
+{
+	const struct csc_adapter *cadapter = a;
+	const struct btd_adapter *adapter = b;
+
+	if (adapter == cadapter->adapter)
+		return 0;
+
+	return -1;
+}
+
+static gint cmp_device(gconstpointer a, gconstpointer b)
+{
+	const struct csc *csc = a;
+	const struct btd_device *dev = b;
+
+	if (dev == csc->dev)
+		return 0;
+
+	return -1;
+}
+
+static struct csc_adapter *find_csc_adapter(struct btd_adapter *adapter)
+{
+	GSList *l = g_slist_find_custom(csc_adapters, adapter, cmp_adapter);
+
+	if (!l)
+		return NULL;
+
+	return l->data;
+}
+
+static void destroy_csc_adapter(gpointer user_data)
+{
+	struct csc_adapter *cadapter = user_data;
+
+	g_free(cadapter);
+}
+
+static void destroy_csc(gpointer user_data)
+{
+	struct csc *csc = user_data;
+
+	btd_device_unref(csc->dev);
+	g_free(csc);
+}
+
+static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+	struct csc_adapter *cadapter;
+
+	cadapter = g_new0(struct csc_adapter, 1);
+	cadapter->adapter = adapter;
+
+	csc_adapters = g_slist_prepend(csc_adapters, cadapter);
+
+	return 0;
+}
+
+static void csc_adapter_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	struct csc_adapter *cadapter;
+
+	cadapter = find_csc_adapter(adapter);
+	if (cadapter == NULL)
+		return;
+
+	csc_adapters = g_slist_remove(csc_adapters, cadapter);
+
+	destroy_csc_adapter(cadapter);
+}
+
+static int csc_device_probe(struct btd_profile *p,
+				struct btd_device *device, GSList *uuids)
+{
+	struct btd_adapter *adapter;
+	struct csc_adapter *cadapter;
+	struct csc *csc;
+
+	adapter = device_get_adapter(device);
+
+	cadapter = find_csc_adapter(adapter);
+	if (cadapter == NULL)
+		return -1;
+
+	csc = g_new0(struct csc, 1);
+	csc->dev = btd_device_ref(device);
+	csc->cadapter = cadapter;
+
+	cadapter->devices = g_slist_prepend(cadapter->devices, csc);
+
+	return 0;
+}
+
+static void csc_device_remove(struct btd_profile *p,
+						struct btd_device *device)
+{
+	struct btd_adapter *adapter;
+	struct csc_adapter *cadapter;
+	struct csc *csc;
+	GSList *l;
+
+	adapter = device_get_adapter(device);
+
+	cadapter = find_csc_adapter(adapter);
+	if (cadapter == NULL)
+		return;
+
+	l = g_slist_find_custom(cadapter->devices, device, cmp_device);
+	if (l == NULL)
+		return;
+
+	csc = l->data;
+
+	cadapter->devices = g_slist_remove(cadapter->devices, csc);
+
+	destroy_csc(csc);
+}
+
+static struct btd_profile cscp_profile = {
+	.name		= "Cycling Speed and Cadence GATT Driver",
+	.remote_uuids	= BTD_UUIDS(CYCLING_SC_UUID),
+
+	.adapter_probe	= csc_adapter_probe,
+	.adapter_remove	= csc_adapter_remove,
+
+	.device_probe	= csc_device_probe,
+	.device_remove	= csc_device_remove,
+};
+
+static int cyclingspeed_init(void)
+{
+	return btd_profile_register(&cscp_profile);
+}
+
+static void cyclingspeed_exit(void)
+{
+	btd_profile_unregister(&cscp_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(cyclingspeed, VERSION,
+					BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+					cyclingspeed_init, cyclingspeed_exit)
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 02/20] cyclingspeed: Add attio callbacks
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 profiles/cyclingspeed/cyclingspeed.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 025b09a..e0f458d 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -33,6 +33,11 @@
 #include "adapter.h"
 #include "device.h"
 #include "profile.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attio.h"
+#include "log.h"
 
 struct csc_adapter {
 	struct btd_adapter	*adapter;
@@ -42,6 +47,9 @@ struct csc_adapter {
 struct csc {
 	struct btd_device	*dev;
 	struct csc_adapter	*cadapter;
+
+	GAttrib			*attrib;
+	guint			attioid;
 };
 
 static GSList *csc_adapters = NULL;
@@ -89,10 +97,35 @@ static void destroy_csc(gpointer user_data)
 {
 	struct csc *csc = user_data;
 
+	if (csc->attioid > 0)
+		btd_device_remove_attio_callback(csc->dev, csc->attioid);
+
+	if (csc->attrib != NULL)
+		g_attrib_unref(csc->attrib);
+
 	btd_device_unref(csc->dev);
 	g_free(csc);
 }
 
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+	struct csc *csc = user_data;
+
+	DBG("");
+
+	csc->attrib = g_attrib_ref(attrib);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+	struct csc *csc = user_data;
+
+	DBG("");
+
+	g_attrib_unref(csc->attrib);
+	csc->attrib = NULL;
+}
+
 static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
 {
 	struct csc_adapter *cadapter;
@@ -138,6 +171,9 @@ static int csc_device_probe(struct btd_profile *p,
 
 	cadapter->devices = g_slist_prepend(cadapter->devices, csc);
 
+	csc->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+						attio_disconnected_cb, csc);
+
 	return 0;
 }
 
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 03/20] cyclingspeed: Discover CSCS characteristics
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 lib/uuid.h                           |  4 +++
 profiles/cyclingspeed/cyclingspeed.c | 60 ++++++++++++++++++++++++++++++++++++
 2 files changed, 64 insertions(+)

diff --git a/lib/uuid.h b/lib/uuid.h
index a9a3b13..1e8188a 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -75,6 +75,10 @@ extern "C" {
 #define MEASUREMENT_INTERVAL_UUID	"00002a21-0000-1000-8000-00805f9b34fb"
 
 #define CYCLING_SC_UUID		"00001816-0000-1000-8000-00805f9b34fb"
+#define CSC_MEASUREMENT_UUID	"00002a5b-0000-1000-8000-00805f9b34fb"
+#define CSC_FEATURE_UUID	"00002a5c-0000-1000-8000-00805f9b34fb"
+#define SENSOR_LOCATION_UUID	"00002a5d-0000-1000-8000-00805f9b34fb"
+#define SC_CONTROL_POINT_UUID	"00002a55-0000-1000-8000-00805f9b34fb"
 
 #define RFCOMM_UUID_STR		"00000003-0000-1000-8000-00805f9b34fb"
 
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index e0f458d..862383a 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -50,6 +50,10 @@ struct csc {
 
 	GAttrib			*attrib;
 	guint			attioid;
+
+	struct att_range	*svc_range;
+
+	uint16_t		controlpoint_val_handle;
 };
 
 static GSList *csc_adapters = NULL;
@@ -104,9 +108,38 @@ static void destroy_csc(gpointer user_data)
 		g_attrib_unref(csc->attrib);
 
 	btd_device_unref(csc->dev);
+	g_free(csc->svc_range);
 	g_free(csc);
 }
 
+static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
+{
+	struct csc *csc = user_data;
+
+	if (status) {
+		error("Discover CSCS characteristics: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	for (; chars; chars = chars->next) {
+		struct gatt_char *c = chars->data;
+
+		if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
+			/* TODO: discover CCC handle */
+		} else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
+			/* TODO: read characterictic value */
+		} else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
+			DBG("Sensor Location supported");
+			/* TODO: read characterictic value */
+		} else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
+			DBG("SC Control Point supported");
+			csc->controlpoint_val_handle = c->value_handle;
+			/* TODO: discover CCC handle */
+		}
+	}
+}
+
 static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
 {
 	struct csc *csc = user_data;
@@ -114,6 +147,10 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
 	DBG("");
 
 	csc->attrib = g_attrib_ref(attrib);
+
+	gatt_discover_char(csc->attrib, csc->svc_range->start,
+				csc->svc_range->end, NULL,
+				discover_char_cb, csc);
 }
 
 static void attio_disconnected_cb(gpointer user_data)
@@ -152,12 +189,31 @@ static void csc_adapter_remove(struct btd_profile *p,
 	destroy_csc_adapter(cadapter);
 }
 
+static gint cmp_primary_uuid(gconstpointer a, gconstpointer b)
+{
+	const struct gatt_primary *prim = a;
+	const char *uuid = b;
+
+	return g_strcmp0(prim->uuid, uuid);
+}
+
 static int csc_device_probe(struct btd_profile *p,
 				struct btd_device *device, GSList *uuids)
 {
 	struct btd_adapter *adapter;
 	struct csc_adapter *cadapter;
 	struct csc *csc;
+	struct gatt_primary *prim;
+	GSList *primaries;
+	GSList *l;
+
+	primaries = btd_device_get_primaries(device);
+
+	l = g_slist_find_custom(primaries, CYCLING_SC_UUID, cmp_primary_uuid);
+	if (l == NULL)
+		return -EINVAL;
+
+	prim = l->data;
 
 	adapter = device_get_adapter(device);
 
@@ -169,6 +225,10 @@ static int csc_device_probe(struct btd_profile *p,
 	csc->dev = btd_device_ref(device);
 	csc->cadapter = cadapter;
 
+	csc->svc_range = g_new0(struct att_range, 1);
+	csc->svc_range->start = prim->range.start;
+	csc->svc_range->end = prim->range.end;
+
 	cadapter->devices = g_slist_prepend(cadapter->devices, csc);
 
 	csc->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 04/20] cyclingspeed: Discover characteristics CCC
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 profiles/cyclingspeed/cyclingspeed.c | 82 +++++++++++++++++++++++++++++++++++-
 1 file changed, 80 insertions(+), 2 deletions(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 862383a..32a088d 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -53,9 +53,15 @@ struct csc {
 
 	struct att_range	*svc_range;
 
+	uint16_t		measurement_ccc_handle;
 	uint16_t		controlpoint_val_handle;
 };
 
+struct characteristic {
+	struct csc	*csc;
+	char		uuid[MAX_LEN_UUID_STR + 1];
+};
+
 static GSList *csc_adapters = NULL;
 
 static gint cmp_adapter(gconstpointer a, gconstpointer b)
@@ -112,6 +118,76 @@ static void destroy_csc(gpointer user_data)
 	g_free(csc);
 }
 
+static void discover_desc_cb(guint8 status, const guint8 *pdu,
+					guint16 len, gpointer user_data)
+{
+	struct characteristic *ch = user_data;
+	struct att_data_list *list = NULL;
+	uint8_t format;
+	int i;
+
+	if (status != 0) {
+		error("Discover %s descriptors failed: %s", ch->uuid,
+							att_ecode2str(status));
+		goto done;
+	}
+
+	list = dec_find_info_resp(pdu, len, &format);
+	if (list == NULL)
+		goto done;
+
+	if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
+		goto done;
+
+	for (i = 0; i < list->num; i++) {
+		uint8_t *value;
+		uint16_t handle, uuid;
+
+		value = list->data[i];
+		handle = att_get_u16(value);
+		uuid = att_get_u16(value + 2);
+
+		if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
+			continue;
+
+		if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0)
+			ch->csc->measurement_ccc_handle = handle;
+
+		/* We only want CCC, can break here */
+		break;
+	}
+
+done:
+	if (list)
+		att_data_list_free(list);
+	g_free(ch);
+}
+
+static void discover_desc(struct csc *csc, struct gatt_char *c,
+						struct gatt_char *c_next)
+{
+	struct characteristic *ch;
+	uint16_t start, end;
+
+	start = c->value_handle + 1;
+
+	if (c_next != NULL) {
+		if (start == c_next->handle)
+			return;
+		end = c_next->handle - 1;
+	} else if (c->value_handle != csc->svc_range->end) {
+		end = csc->svc_range->end;
+	} else {
+		return;
+	}
+
+	ch = g_new0(struct characteristic, 1);
+	ch->csc = csc;
+	memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
+
+	gatt_find_info(csc->attrib, start, end, discover_desc_cb, ch);
+}
+
 static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
 {
 	struct csc *csc = user_data;
@@ -124,9 +200,11 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
 
 	for (; chars; chars = chars->next) {
 		struct gatt_char *c = chars->data;
+		struct gatt_char *c_next =
+				(chars->next ? chars->next->data : NULL);
 
 		if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
-			/* TODO: discover CCC handle */
+			discover_desc(csc, c, c_next);
 		} else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
 			/* TODO: read characterictic value */
 		} else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
@@ -135,7 +213,7 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
 		} else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
 			DBG("SC Control Point supported");
 			csc->controlpoint_val_handle = c->value_handle;
-			/* TODO: discover CCC handle */
+			discover_desc(csc, c, c_next);
 		}
 	}
 }
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 05/20] cyclingspeed: Read CSC Feature characteristic value
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 profiles/cyclingspeed/cyclingspeed.c | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 32a088d..7f79116 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -55,6 +55,8 @@ struct csc {
 
 	uint16_t		measurement_ccc_handle;
 	uint16_t		controlpoint_val_handle;
+
+	uint16_t		feature;
 };
 
 struct characteristic {
@@ -118,6 +120,32 @@ static void destroy_csc(gpointer user_data)
 	g_free(csc);
 }
 
+static void read_feature_cb(guint8 status, const guint8 *pdu,
+						guint16 len, gpointer user_data)
+{
+	struct csc *csc = user_data;
+	uint8_t value[2];
+	ssize_t vlen;
+
+	if (status) {
+		error("CSC Feature read failed: %s", att_ecode2str(status));
+		return;
+	}
+
+	vlen = dec_read_resp(pdu, len, value, sizeof(value));
+	if (vlen < 0) {
+		error("Protocol error");
+		return;
+	}
+
+	if (vlen != sizeof(value)) {
+		error("Invalid value length for CSC Feature");
+		return;
+	}
+
+	csc->feature = att_get_u16(value);
+}
+
 static void discover_desc_cb(guint8 status, const guint8 *pdu,
 					guint16 len, gpointer user_data)
 {
@@ -191,6 +219,7 @@ static void discover_desc(struct csc *csc, struct gatt_char *c,
 static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
 {
 	struct csc *csc = user_data;
+	uint16_t feature_val_handle = 0;
 
 	if (status) {
 		error("Discover CSCS characteristics: %s",
@@ -206,7 +235,7 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
 		if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
 			discover_desc(csc, c, c_next);
 		} else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
-			/* TODO: read characterictic value */
+			feature_val_handle = c->value_handle;
 		} else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
 			DBG("Sensor Location supported");
 			/* TODO: read characterictic value */
@@ -216,6 +245,10 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
 			discover_desc(csc, c, c_next);
 		}
 	}
+
+	if (feature_val_handle > 0)
+		gatt_read_char(csc->attrib, feature_val_handle,
+							read_feature_cb, csc);
 }
 
 static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 06/20] cyclingspeed: Read Sensor Location characteristic value
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 profiles/cyclingspeed/cyclingspeed.c | 40 +++++++++++++++++++++++++++++++++++-
 1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 7f79116..ec52ab4 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -24,6 +24,7 @@
 #include <config.h>
 #endif
 
+#include <gdbus.h>
 #include <errno.h>
 #include <stdbool.h>
 #include <glib.h>
@@ -33,12 +34,15 @@
 #include "adapter.h"
 #include "device.h"
 #include "profile.h"
+#include "dbus-common.h"
 #include "attrib/gattrib.h"
 #include "attrib/att.h"
 #include "attrib/gatt.h"
 #include "attio.h"
 #include "log.h"
 
+#define CYCLINGSPEED_INTERFACE		"org.bluez.CyclingSpeed"
+
 struct csc_adapter {
 	struct btd_adapter	*adapter;
 	GSList			*devices;	/* list of registered devices */
@@ -57,6 +61,8 @@ struct csc {
 	uint16_t		controlpoint_val_handle;
 
 	uint16_t		feature;
+	gboolean		has_location;
+	uint8_t			location;
 };
 
 struct characteristic {
@@ -146,6 +152,37 @@ static void read_feature_cb(guint8 status, const guint8 *pdu,
 	csc->feature = att_get_u16(value);
 }
 
+static void read_location_cb(guint8 status, const guint8 *pdu,
+						guint16 len, gpointer user_data)
+{
+	struct csc *csc = user_data;
+	uint8_t value;
+	ssize_t vlen;
+
+	if (status) {
+		error("Sensor Location read failed: %s", att_ecode2str(status));
+		return;
+	}
+
+	vlen = dec_read_resp(pdu, len, &value, sizeof(value));
+	if (vlen < 0) {
+		error("Protocol error");
+		return;
+	}
+
+	if (vlen != sizeof(value)) {
+		error("Invalid value length for Sensor Location");
+		return;
+	}
+
+	csc->has_location = TRUE;
+	csc->location = value;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					device_get_path(csc->dev),
+					CYCLINGSPEED_INTERFACE, "Location");
+}
+
 static void discover_desc_cb(guint8 status, const guint8 *pdu,
 					guint16 len, gpointer user_data)
 {
@@ -238,7 +275,8 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
 			feature_val_handle = c->value_handle;
 		} else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
 			DBG("Sensor Location supported");
-			/* TODO: read characterictic value */
+			gatt_read_char(csc->attrib, c->value_handle,
+							read_location_cb, csc);
 		} else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
 			DBG("SC Control Point supported");
 			csc->controlpoint_val_handle = c->value_handle;
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 07/20] cyclingspeed: Add CyclingSpeedManager interface
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

This patch registers org.bluez.CyclingSpeedManager interface for each
adapter to allow registration and deregistration of measurement watchers.
---
 profiles/cyclingspeed/cyclingspeed.c | 151 ++++++++++++++++++++++++++++++++++-
 1 file changed, 150 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index ec52ab4..cf58a22 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -35,6 +35,7 @@
 #include "device.h"
 #include "profile.h"
 #include "dbus-common.h"
+#include "error.h"
 #include "attrib/gattrib.h"
 #include "attrib/att.h"
 #include "attrib/gatt.h"
@@ -42,10 +43,12 @@
 #include "log.h"
 
 #define CYCLINGSPEED_INTERFACE		"org.bluez.CyclingSpeed"
+#define CYCLINGSPEED_MANAGER_INTERFACE	"org.bluez.CyclingSpeedManager"
 
 struct csc_adapter {
 	struct btd_adapter	*adapter;
 	GSList			*devices;	/* list of registered devices */
+	GSList			*watchers;
 };
 
 struct csc {
@@ -65,6 +68,13 @@ struct csc {
 	uint8_t			location;
 };
 
+struct watcher {
+	struct csc_adapter	*cadapter;
+	guint			id;
+	char			*srv;
+	char			*path;
+};
+
 struct characteristic {
 	struct csc	*csc;
 	char		uuid[MAX_LEN_UUID_STR + 1];
@@ -94,6 +104,19 @@ static gint cmp_device(gconstpointer a, gconstpointer b)
 	return -1;
 }
 
+static gint cmp_watcher(gconstpointer a, gconstpointer b)
+{
+	const struct watcher *watcher = a;
+	const struct watcher *match = b;
+	int ret;
+
+	ret = g_strcmp0(watcher->srv, match->srv);
+	if (ret != 0)
+		return ret;
+
+	return g_strcmp0(watcher->path, match->path);
+}
+
 static struct csc_adapter *find_csc_adapter(struct btd_adapter *adapter)
 {
 	GSList *l = g_slist_find_custom(csc_adapters, adapter, cmp_adapter);
@@ -104,10 +127,47 @@ static struct csc_adapter *find_csc_adapter(struct btd_adapter *adapter)
 	return l->data;
 }
 
+static void destroy_watcher(gpointer user_data)
+{
+	struct watcher *watcher = user_data;
+
+	g_free(watcher->path);
+	g_free(watcher->srv);
+	g_free(watcher);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+							const char *path)
+{
+	struct watcher *match;
+	GSList *l;
+
+	match = g_new0(struct watcher, 1);
+	match->srv = g_strdup(sender);
+	match->path = g_strdup(path);
+
+	l = g_slist_find_custom(list, match, cmp_watcher);
+	destroy_watcher(match);
+
+	if (l != NULL)
+		return l->data;
+
+	return NULL;
+}
+
+static void remove_watcher(gpointer user_data)
+{
+	struct watcher *watcher = user_data;
+
+	g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+}
+
 static void destroy_csc_adapter(gpointer user_data)
 {
 	struct csc_adapter *cadapter = user_data;
 
+	g_slist_free_full(cadapter->watchers, remove_watcher);
+
 	g_free(cadapter);
 }
 
@@ -312,6 +372,81 @@ static void attio_disconnected_cb(gpointer user_data)
 	csc->attrib = NULL;
 }
 
+static void watcher_exit_cb(DBusConnection *conn, void *user_data)
+{
+	struct watcher *watcher = user_data;
+	struct csc_adapter *cadapter = watcher->cadapter;
+
+	DBG("cycling watcher [%s] disconnected", watcher->path);
+
+	cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
+	g_dbus_remove_watch(conn, watcher->id);
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct csc_adapter *cadapter = data;
+	struct watcher *watcher;
+	const char *sender = dbus_message_get_sender(msg);
+	char *path;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	watcher = find_watcher(cadapter->watchers, sender, path);
+	if (watcher != NULL)
+		return btd_error_already_exists(msg);
+
+	watcher = g_new0(struct watcher, 1);
+	watcher->cadapter = cadapter;
+	watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb,
+						watcher, destroy_watcher);
+	watcher->srv = g_strdup(sender);
+	watcher->path = g_strdup(path);
+
+	cadapter->watchers = g_slist_prepend(cadapter->watchers, watcher);
+
+	DBG("cycling watcher [%s] registered", path);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct csc_adapter *cadapter = data;
+	struct watcher *watcher;
+	const char *sender = dbus_message_get_sender(msg);
+	char *path;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	watcher = find_watcher(cadapter->watchers, sender, path);
+	if (watcher == NULL)
+		return btd_error_does_not_exist(msg);
+
+	cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
+	g_dbus_remove_watch(conn, watcher->id);
+
+	DBG("cycling watcher [%s] unregistered", path);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable cyclingspeed_manager_methods[] = {
+	{ GDBUS_METHOD("RegisterWatcher",
+			GDBUS_ARGS({ "agent", "o" }), NULL,
+			register_watcher) },
+	{ GDBUS_METHOD("UnregisterWatcher",
+			GDBUS_ARGS({ "agent", "o" }), NULL,
+			unregister_watcher) },
+	{ }
+};
+
 static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
 {
 	struct csc_adapter *cadapter;
@@ -321,6 +456,18 @@ static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
 
 	csc_adapters = g_slist_prepend(csc_adapters, cadapter);
 
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+						adapter_get_path(adapter),
+						CYCLINGSPEED_MANAGER_INTERFACE,
+						cyclingspeed_manager_methods,
+						NULL, NULL, cadapter,
+						destroy_csc_adapter)) {
+		error("D-Bus failed to register %s interface",
+						CYCLINGSPEED_MANAGER_INTERFACE);
+		destroy_csc_adapter(cadapter);
+		return -EIO;
+	}
+
 	return 0;
 }
 
@@ -335,7 +482,9 @@ static void csc_adapter_remove(struct btd_profile *p,
 
 	csc_adapters = g_slist_remove(csc_adapters, cadapter);
 
-	destroy_csc_adapter(cadapter);
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+					adapter_get_path(cadapter->adapter),
+					CYCLINGSPEED_MANAGER_INTERFACE);
 }
 
 static gint cmp_primary_uuid(gconstpointer a, gconstpointer b)
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 08/20] cyclingspeed: Add support to enable measurement notifications
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 profiles/cyclingspeed/cyclingspeed.c | 72 +++++++++++++++++++++++++++++++++++-
 1 file changed, 71 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index cf58a22..5cb4ffd 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -186,6 +186,17 @@ static void destroy_csc(gpointer user_data)
 	g_free(csc);
 }
 
+static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	char *msg = user_data;
+
+	if (status != 0)
+		error("%s failed", msg);
+
+	g_free(msg);
+}
+
 static void read_feature_cb(guint8 status, const guint8 *pdu,
 						guint16 len, gpointer user_data)
 {
@@ -275,9 +286,25 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
 		if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
 			continue;
 
-		if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0)
+		if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
+			char *msg;
+			uint8_t attr_val[2];
+
 			ch->csc->measurement_ccc_handle = handle;
 
+			if (g_slist_length(ch->csc->cadapter->watchers) == 0) {
+				att_put_u16(0x0000, attr_val);
+				msg = g_strdup("Disable measurement");
+			} else {
+				att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT,
+								attr_val);
+				msg = g_strdup("Enable measurement");
+			}
+
+			gatt_write_char(ch->csc->attrib, handle, attr_val,
+					sizeof(attr_val), char_write_cb, msg);
+		}
+
 		/* We only want CCC, can break here */
 		break;
 	}
@@ -349,6 +376,40 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
 							read_feature_cb, csc);
 }
 
+static void enable_measurement(gpointer data, gpointer user_data)
+{
+	struct csc *csc = data;
+	uint16_t handle = csc->measurement_ccc_handle;
+	uint8_t value[2];
+	char *msg;
+
+	if (csc->attrib == NULL || !handle)
+		return;
+
+	att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+	msg = g_strdup("Enable measurement");
+
+	gatt_write_char(csc->attrib, handle, value, sizeof(value),
+							char_write_cb, msg);
+}
+
+static void disable_measurement(gpointer data, gpointer user_data)
+{
+	struct csc *csc = data;
+	uint16_t handle = csc->measurement_ccc_handle;
+	uint8_t value[2];
+	char *msg;
+
+	if (csc->attrib == NULL || !handle)
+		return;
+
+	att_put_u16(0x0000, value);
+	msg = g_strdup("Disable measurement");
+
+	gatt_write_char(csc->attrib, handle, value, sizeof(value),
+							char_write_cb, msg);
+}
+
 static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
 {
 	struct csc *csc = user_data;
@@ -381,6 +442,9 @@ static void watcher_exit_cb(DBusConnection *conn, void *user_data)
 
 	cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
 	g_dbus_remove_watch(conn, watcher->id);
+
+	if (g_slist_length(cadapter->watchers) == 0)
+		g_slist_foreach(cadapter->devices, disable_measurement, 0);
 }
 
 static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
@@ -406,6 +470,9 @@ static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
 	watcher->srv = g_strdup(sender);
 	watcher->path = g_strdup(path);
 
+	if (g_slist_length(cadapter->watchers) == 0)
+		g_slist_foreach(cadapter->devices, enable_measurement, 0);
+
 	cadapter->watchers = g_slist_prepend(cadapter->watchers, watcher);
 
 	DBG("cycling watcher [%s] registered", path);
@@ -432,6 +499,9 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
 	cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
 	g_dbus_remove_watch(conn, watcher->id);
 
+	if (g_slist_length(cadapter->watchers) == 0)
+		g_slist_foreach(cadapter->devices, disable_measurement, 0);
+
 	DBG("cycling watcher [%s] unregistered", path);
 
 	return dbus_message_new_method_return(msg);
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 09/20] cyclingspeed: Process measurement notifications
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 profiles/cyclingspeed/cyclingspeed.c | 139 ++++++++++++++++++++++++++++++++++-
 1 file changed, 138 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 5cb4ffd..a3e2d61 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -44,6 +44,14 @@
 
 #define CYCLINGSPEED_INTERFACE		"org.bluez.CyclingSpeed"
 #define CYCLINGSPEED_MANAGER_INTERFACE	"org.bluez.CyclingSpeedManager"
+#define CYCLINGSPEED_WATCHER_INTERFACE	"org.bluez.CyclingSpeedWatcher"
+
+#define WHEEL_REV_SUPPORT		0x01
+#define CRANK_REV_SUPPORT		0x02
+#define MULTI_SENSOR_LOC_SUPPORT	0x04
+
+#define WHEEL_REV_PRESENT	0x01
+#define CRANK_REV_PRESENT	0x02
 
 struct csc_adapter {
 	struct btd_adapter	*adapter;
@@ -57,6 +65,8 @@ struct csc {
 
 	GAttrib			*attrib;
 	guint			attioid;
+	/* attio id for measurement characteristics value notifications */
+	guint			attio_measurement_id;
 
 	struct att_range	*svc_range;
 
@@ -75,6 +85,18 @@ struct watcher {
 	char			*path;
 };
 
+struct measurement {
+	struct csc	*csc;
+
+	bool		has_wheel_rev;
+	uint32_t	wheel_rev;
+	uint16_t	last_wheel_time;
+
+	bool		has_crank_rev;
+	uint16_t	crank_rev;
+	uint16_t	last_crank_time;
+};
+
 struct characteristic {
 	struct csc	*csc;
 	char		uuid[MAX_LEN_UUID_STR + 1];
@@ -178,8 +200,10 @@ static void destroy_csc(gpointer user_data)
 	if (csc->attioid > 0)
 		btd_device_remove_attio_callback(csc->dev, csc->attioid);
 
-	if (csc->attrib != NULL)
+	if (csc->attrib != NULL) {
+		g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
 		g_attrib_unref(csc->attrib);
+	}
 
 	btd_device_unref(csc->dev);
 	g_free(csc->svc_range);
@@ -340,6 +364,109 @@ static void discover_desc(struct csc *csc, struct gatt_char *c,
 	gatt_find_info(csc->attrib, start, end, discover_desc_cb, ch);
 }
 
+static void update_watcher(gpointer data, gpointer user_data)
+{
+	struct watcher *w = data;
+	struct measurement *m = user_data;
+	struct csc *csc = m->csc;
+	const gchar *path = device_get_path(csc->dev);
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(w->srv, w->path,
+			CYCLINGSPEED_WATCHER_INTERFACE, "MeasurementReceived");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	if (m->has_wheel_rev) {
+		dict_append_entry(&dict, "WheelRevolutions",
+					DBUS_TYPE_UINT32, &m->wheel_rev);
+		dict_append_entry(&dict, "LastWheelEventTime",
+					DBUS_TYPE_UINT16, &m->last_wheel_time);
+	}
+
+	if (m->has_crank_rev) {
+		dict_append_entry(&dict, "CrankRevolutions",
+					DBUS_TYPE_UINT16, &m->crank_rev);
+		dict_append_entry(&dict, "LastCrankEventTime",
+					DBUS_TYPE_UINT16, &m->last_crank_time);
+	}
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void process_measurement(struct csc *csc, const uint8_t *pdu,
+								uint16_t len)
+{
+	struct measurement m;
+	uint8_t flags;
+
+	flags = *pdu;
+
+	pdu++;
+	len--;
+
+	memset(&m, 0, sizeof(m));
+
+	if ((flags & WHEEL_REV_PRESENT) && (csc->feature & WHEEL_REV_SUPPORT)) {
+		if (len < 6) {
+			error("Wheel revolutions data fields missing");
+			return;
+		}
+
+		m.has_wheel_rev = true;
+		m.wheel_rev = att_get_u32(pdu);
+		m.last_wheel_time = att_get_u16(pdu + 4);
+		pdu += 6;
+		len -= 6;
+	}
+
+	if ((flags & CRANK_REV_PRESENT) && (csc->feature & CRANK_REV_SUPPORT)) {
+		if (len < 4) {
+			error("Crank revolutions data fields missing");
+			return;
+		}
+
+		m.has_crank_rev = true;
+		m.crank_rev = att_get_u16(pdu);
+		m.last_crank_time = att_get_u16(pdu + 2);
+		pdu += 4;
+		len -= 4;
+	}
+
+	/* Notify all registered watchers */
+	m.csc = csc;
+	g_slist_foreach(csc->cadapter->watchers, update_watcher, &m);
+}
+
+static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
+							gpointer user_data)
+{
+	struct csc *csc = user_data;
+
+	/* should be at least opcode (1b) + handle (2b) */
+	if (len < 3) {
+		error("Invalid PDU received");
+		return;
+	}
+
+	process_measurement(csc, pdu + 3, len - 3);
+}
+
+
 static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
 {
 	struct csc *csc = user_data;
@@ -357,6 +484,11 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
 				(chars->next ? chars->next->data : NULL);
 
 		if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
+			csc->attio_measurement_id =
+				g_attrib_register(csc->attrib,
+					ATT_OP_HANDLE_NOTIFY, c->value_handle,
+					measurement_notify_handler, csc, NULL);
+
 			discover_desc(csc, c, c_next);
 		} else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
 			feature_val_handle = c->value_handle;
@@ -429,6 +561,11 @@ static void attio_disconnected_cb(gpointer user_data)
 
 	DBG("");
 
+	if (csc->attio_measurement_id > 0) {
+		g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
+		csc->attio_measurement_id = 0;
+	}
+
 	g_attrib_unref(csc->attrib);
 	csc->attrib = NULL;
 }
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 10/20] cyclingspeed: Add DBus.Properties for org.bluez.CyclingSpeed interface
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 profiles/cyclingspeed/cyclingspeed.c | 96 +++++++++++++++++++++++++++++++++++-
 1 file changed, 95 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index a3e2d61..eace558 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -104,6 +104,21 @@ struct characteristic {
 
 static GSList *csc_adapters = NULL;
 
+static const char * const location_enum[] = {
+	"other", "top-of-shoe", "in-shoe", "hip", "front-wheel", "left-crank",
+	"right-crank", "left-pedal", "right-pedal", "front-hub",
+	"rear-dropout", "chainstay", "rear-wheel", "rear-hub"
+};
+
+static const gchar *location2str(uint8_t value)
+{
+	if (value < G_N_ELEMENTS(location_enum))
+		return location_enum[value];
+
+	info("Body Sensor Location [%d] is RFU", value);
+	return location_enum[0];
+}
+
 static gint cmp_adapter(gconstpointer a, gconstpointer b)
 {
 	const struct csc_adapter *cadapter = a;
@@ -694,6 +709,72 @@ static void csc_adapter_remove(struct btd_profile *p,
 					CYCLINGSPEED_MANAGER_INTERFACE);
 }
 
+static gboolean property_get_location(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct csc *csc = data;
+	const char *loc;
+
+	if (!csc->has_location)
+		return FALSE;
+
+	loc = location2str(csc->location);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc);
+
+	return TRUE;
+}
+
+static gboolean property_exists_location(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct csc *csc = data;
+
+	return csc->has_location;
+}
+
+static gboolean property_exists_locations(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct csc *csc = data;
+
+	return !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
+}
+
+static gboolean property_get_wheel_rev_sup(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct csc *csc = data;
+	dbus_bool_t val;
+
+	val = !!(csc->feature & WHEEL_REV_SUPPORT);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+	return TRUE;
+}
+
+static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct csc *csc = data;
+	dbus_bool_t val;
+
+	val = !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable cyclingspeed_device_properties[] = {
+	{ "Location", "s", property_get_location, NULL,
+						property_exists_location },
+	{ "SupportedLocations", "as", NULL, NULL,
+						property_exists_locations },
+	{ "WheelRevolutionDataSupported", "b", property_get_wheel_rev_sup },
+	{ "MultipleLocationsSupported", "b", property_get_multi_loc_sup },
+	{ }
+};
+
 static gint cmp_primary_uuid(gconstpointer a, gconstpointer b)
 {
 	const struct gatt_primary *prim = a;
@@ -730,6 +811,18 @@ static int csc_device_probe(struct btd_profile *p,
 	csc->dev = btd_device_ref(device);
 	csc->cadapter = cadapter;
 
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+						device_get_path(device),
+						CYCLINGSPEED_INTERFACE,
+						NULL, NULL,
+						cyclingspeed_device_properties,
+						csc, destroy_csc)) {
+		error("D-Bus failed to register %s interface",
+						CYCLINGSPEED_INTERFACE);
+		destroy_csc(csc);
+		return -EIO;
+	}
+
 	csc->svc_range = g_new0(struct att_range, 1);
 	csc->svc_range->start = prim->range.start;
 	csc->svc_range->end = prim->range.end;
@@ -764,7 +857,8 @@ static void csc_device_remove(struct btd_profile *p,
 
 	cadapter->devices = g_slist_remove(cadapter->devices, csc);
 
-	destroy_csc(csc);
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+			device_get_path(device), CYCLINGSPEED_INTERFACE);
 }
 
 static struct btd_profile cscp_profile = {
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 11/20] cyclingspeed: Add stub to use SC Control Point
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

This patch implements common functions to use SC Control Point.
Individual procedures will be implemented in subsequent patches.
---
 profiles/cyclingspeed/cyclingspeed.c | 145 +++++++++++++++++++++++++++++++++--
 1 file changed, 140 insertions(+), 5 deletions(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index eace558..866fac8 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -42,6 +42,11 @@
 #include "attio.h"
 #include "log.h"
 
+/* min length for ATT indication or notification: opcode (1b) + handle (2b) */
+#define ATT_HDR_LEN 3
+
+#define ATT_TIMEOUT 30
+
 #define CYCLINGSPEED_INTERFACE		"org.bluez.CyclingSpeed"
 #define CYCLINGSPEED_MANAGER_INTERFACE	"org.bluez.CyclingSpeedManager"
 #define CYCLINGSPEED_WATCHER_INTERFACE	"org.bluez.CyclingSpeedWatcher"
@@ -53,6 +58,25 @@
 #define WHEEL_REV_PRESENT	0x01
 #define CRANK_REV_PRESENT	0x02
 
+#define SET_CUMULATIVE_VALUE		0x01
+#define START_SENSOR_CALIBRATION	0x02
+#define UPDATE_SENSOR_LOC		0x03
+#define REQUEST_SUPPORTED_SENSOR_LOC	0x04
+#define RESPONSE_CODE			0x10
+
+#define RSP_SUCCESS		0x01
+#define RSP_NOT_SUPPORTED	0x02
+#define RSP_INVALID_PARAM	0x03
+#define RSP_FAILED		0x04
+
+struct csc;
+
+struct controlpoint_req {
+	struct csc		*csc;
+	uint8_t			opcode;
+	guint			timeout;
+};
+
 struct csc_adapter {
 	struct btd_adapter	*adapter;
 	GSList			*devices;	/* list of registered devices */
@@ -67,6 +91,8 @@ struct csc {
 	guint			attioid;
 	/* attio id for measurement characteristics value notifications */
 	guint			attio_measurement_id;
+	/* attio id for SC Control Point characteristics value indications */
+	guint			attio_controlpoint_id;
 
 	struct att_range	*svc_range;
 
@@ -76,6 +102,8 @@ struct csc {
 	uint16_t		feature;
 	gboolean		has_location;
 	uint8_t			location;
+
+	struct controlpoint_req	*pending_req;
 };
 
 struct watcher {
@@ -217,6 +245,7 @@ static void destroy_csc(gpointer user_data)
 
 	if (csc->attrib != NULL) {
 		g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
+		g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
 		g_attrib_unref(csc->attrib);
 	}
 
@@ -236,6 +265,35 @@ static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
 	g_free(msg);
 }
 
+static gboolean controlpoint_timeout(gpointer user_data)
+{
+	struct controlpoint_req *req = user_data;
+
+	req->csc->pending_req = NULL;
+	g_free(req);
+
+	return FALSE;
+}
+
+__attribute__((unused)) /* TODO: remove once controlpoint ops are implemented */
+static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct controlpoint_req *req = user_data;
+
+	if (status != 0) {
+		error("SC Control Point write failed (opcode=%d)", req->opcode);
+
+		req->csc->pending_req = NULL;
+		g_free(req);
+
+		return;
+	}
+
+	req->timeout = g_timeout_add_seconds(ATT_TIMEOUT, controlpoint_timeout,
+									req);
+}
+
 static void read_feature_cb(guint8 status, const guint8 *pdu,
 						guint16 len, gpointer user_data)
 {
@@ -317,6 +375,8 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
 	for (i = 0; i < list->num; i++) {
 		uint8_t *value;
 		uint16_t handle, uuid;
+		uint8_t attr_val[2];
+		char *msg;
 
 		value = list->data[i];
 		handle = att_get_u16(value);
@@ -326,9 +386,6 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
 			continue;
 
 		if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
-			char *msg;
-			uint8_t attr_val[2];
-
 			ch->csc->measurement_ccc_handle = handle;
 
 			if (g_slist_length(ch->csc->cadapter->watchers) == 0) {
@@ -340,10 +397,16 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
 				msg = g_strdup("Enable measurement");
 			}
 
-			gatt_write_char(ch->csc->attrib, handle, attr_val,
-					sizeof(attr_val), char_write_cb, msg);
+		} else if (g_strcmp0(ch->uuid, SC_CONTROL_POINT_UUID) == 0) {
+			att_put_u16(GATT_CLIENT_CHARAC_CFG_IND_BIT, attr_val);
+			msg = g_strdup("Enable SC Control Point indications");
+		} else {
+			break;
 		}
 
+		gatt_write_char(ch->csc->attrib, handle, attr_val,
+					sizeof(attr_val), char_write_cb, msg);
+
 		/* We only want CCC, can break here */
 		break;
 	}
@@ -481,6 +544,67 @@ static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
 	process_measurement(csc, pdu + 3, len - 3);
 }
 
+static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
+							gpointer user_data)
+{
+	struct csc *csc = user_data;
+	struct controlpoint_req *req = csc->pending_req;
+	uint8_t opcode;
+	uint8_t req_opcode;
+	uint8_t *opdu;
+	uint16_t olen;
+	size_t plen;
+
+	if (len < ATT_HDR_LEN) {
+		error("Invalid PDU received");
+		return;
+	}
+
+	/* skip ATT header */
+	pdu += ATT_HDR_LEN;
+	len -= ATT_HDR_LEN;
+
+	if (len < 1) {
+		error("Op Code missing");
+		goto done;
+	}
+
+	opcode = *pdu;
+	pdu++;
+	len--;
+
+	if (opcode != RESPONSE_CODE) {
+		DBG("Unsupported Op Code received (%d)", opcode);
+		goto done;
+	}
+
+	if (len < 2) {
+		error("Invalid Response Code PDU received");
+		goto done;
+	}
+
+	req_opcode = *pdu;
+	/* skip response code for now */
+	pdu += 2;
+	len -= 2;
+
+	if (req == NULL || req->opcode != req_opcode) {
+		DBG("Indication received without pending request");
+		goto done;
+	}
+
+	/* TODO: handle response */
+
+	csc->pending_req = NULL;
+	g_source_remove(req->timeout);
+	g_free(req);
+
+done:
+	opdu = g_attrib_get_buffer(csc->attrib, &plen);
+	olen = enc_confirmation(opdu, plen);
+	if (olen > 0)
+		g_attrib_send(csc->attrib, 0, opdu, olen, NULL, NULL, NULL);
+}
 
 static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
 {
@@ -514,6 +638,12 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
 		} else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
 			DBG("SC Control Point supported");
 			csc->controlpoint_val_handle = c->value_handle;
+
+			csc->attio_controlpoint_id = g_attrib_register(
+					csc->attrib, ATT_OP_HANDLE_IND,
+					c->value_handle,
+					controlpoint_ind_handler, csc, NULL);
+
 			discover_desc(csc, c, c_next);
 		}
 	}
@@ -581,6 +711,11 @@ static void attio_disconnected_cb(gpointer user_data)
 		csc->attio_measurement_id = 0;
 	}
 
+	if (csc->attio_controlpoint_id > 0) {
+		g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
+		csc->attio_controlpoint_id = 0;
+	}
+
 	g_attrib_unref(csc->attrib);
 	csc->attrib = NULL;
 }
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 12/20] cyclingspeed: Add support for Request Supported Sensor Locations
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 profiles/cyclingspeed/cyclingspeed.c | 61 +++++++++++++++++++++++++++++++++---
 1 file changed, 57 insertions(+), 4 deletions(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 866fac8..8283894 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -102,6 +102,8 @@ struct csc {
 	uint16_t		feature;
 	gboolean		has_location;
 	uint8_t			location;
+	uint8_t			num_locations;
+	uint8_t			*locations;
 
 	struct controlpoint_req	*pending_req;
 };
@@ -251,6 +253,7 @@ static void destroy_csc(gpointer user_data)
 
 	btd_device_unref(csc->dev);
 	g_free(csc->svc_range);
+	g_free(csc->locations);
 	g_free(csc);
 }
 
@@ -275,7 +278,6 @@ static gboolean controlpoint_timeout(gpointer user_data)
 	return FALSE;
 }
 
-__attribute__((unused)) /* TODO: remove once controlpoint ops are implemented */
 static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
 							gpointer user_data)
 {
@@ -294,6 +296,20 @@ static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
 									req);
 }
 
+static void read_supported_locations(struct csc *csc)
+{
+	struct controlpoint_req *req;
+
+	req = g_new0(struct controlpoint_req, 1);
+	req->csc = csc;
+	req->opcode = REQUEST_SUPPORTED_SENSOR_LOC;
+
+	csc->pending_req = req;
+
+	gatt_write_char(csc->attrib, csc->controlpoint_val_handle, &req->opcode,
+			sizeof(req->opcode), controlpoint_write_cb, req);
+}
+
 static void read_feature_cb(guint8 status, const guint8 *pdu,
 						guint16 len, gpointer user_data)
 {
@@ -318,6 +334,10 @@ static void read_feature_cb(guint8 status, const guint8 *pdu,
 	}
 
 	csc->feature = att_get_u16(value);
+
+	if ((csc->feature & MULTI_SENSOR_LOC_SUPPORT)
+						&& (csc->locations == NULL))
+		read_supported_locations(csc);
 }
 
 static void read_location_cb(guint8 status, const guint8 *pdu,
@@ -551,6 +571,7 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
 	struct controlpoint_req *req = csc->pending_req;
 	uint8_t opcode;
 	uint8_t req_opcode;
+	uint8_t rsp_code;
 	uint8_t *opdu;
 	uint16_t olen;
 	size_t plen;
@@ -584,7 +605,7 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
 	}
 
 	req_opcode = *pdu;
-	/* skip response code for now */
+	rsp_code = *(pdu + 1);
 	pdu += 2;
 	len -= 2;
 
@@ -593,7 +614,16 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
 		goto done;
 	}
 
-	/* TODO: handle response */
+	switch (req->opcode) {
+	case REQUEST_SUPPORTED_SENSOR_LOC:
+		if (rsp_code == RSP_SUCCESS) {
+			csc->num_locations = len;
+			csc->locations = g_memdup(pdu, len);
+		} else {
+			error("Failed to read Supported Sendor Locations");
+		}
+		break;
+	}
 
 	csc->pending_req = NULL;
 	g_source_remove(req->timeout);
@@ -868,6 +898,29 @@ static gboolean property_exists_location(const GDBusPropertyTable *property,
 	return csc->has_location;
 }
 
+static gboolean property_get_locations(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct csc *csc = data;
+	DBusMessageIter entry;
+	int i;
+
+	if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT))
+		return FALSE;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_STRING_AS_STRING, &entry);
+	for (i = 0; i < csc->num_locations; i++) {
+		char *loc = g_strdup(location2str(csc->locations[i]));
+		dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &loc);
+		g_free(loc);
+	}
+
+	dbus_message_iter_close_container(iter, &entry);
+
+	return TRUE;
+}
+
 static gboolean property_exists_locations(const GDBusPropertyTable *property,
 								void *data)
 {
@@ -903,7 +956,7 @@ static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
 static const GDBusPropertyTable cyclingspeed_device_properties[] = {
 	{ "Location", "s", property_get_location, NULL,
 						property_exists_location },
-	{ "SupportedLocations", "as", NULL, NULL,
+	{ "SupportedLocations", "as", property_get_locations, NULL,
 						property_exists_locations },
 	{ "WheelRevolutionDataSupported", "b", property_get_wheel_rev_sup },
 	{ "MultipleLocationsSupported", "b", property_get_multi_loc_sup },
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 13/20] cyclingspeed: Add support for Update Sensor Location
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 profiles/cyclingspeed/cyclingspeed.c | 122 ++++++++++++++++++++++++++++++++++-
 1 file changed, 121 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 8283894..58ccf74 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -75,6 +75,9 @@ struct controlpoint_req {
 	struct csc		*csc;
 	uint8_t			opcode;
 	guint			timeout;
+	GDBusPendingReply	reply_id;
+
+	uint8_t			pending_location;
 };
 
 struct csc_adapter {
@@ -149,6 +152,17 @@ static const gchar *location2str(uint8_t value)
 	return location_enum[0];
 }
 
+static int str2location(const char *location)
+{
+	size_t i;
+
+	for (i = 0; i < G_N_ELEMENTS(location_enum); i++)
+		if (!strcmp(location_enum[i], location))
+			return i;
+
+	return -1;
+}
+
 static gint cmp_adapter(gconstpointer a, gconstpointer b)
 {
 	const struct csc_adapter *cadapter = a;
@@ -272,6 +286,12 @@ static gboolean controlpoint_timeout(gpointer user_data)
 {
 	struct controlpoint_req *req = user_data;
 
+	if (req->opcode == UPDATE_SENSOR_LOC) {
+		g_dbus_pending_property_error(req->reply_id,
+				ERROR_INTERFACE ".Failed",
+				"Operation failed (timeout)");
+	}
+
 	req->csc->pending_req = NULL;
 	g_free(req);
 
@@ -286,6 +306,12 @@ static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
 	if (status != 0) {
 		error("SC Control Point write failed (opcode=%d)", req->opcode);
 
+		if (req->opcode == UPDATE_SENSOR_LOC) {
+			g_dbus_pending_property_error(req->reply_id,
+					ERROR_INTERFACE ".Failed",
+					"Operation failed (%d)", status);
+		}
+
 		req->csc->pending_req = NULL;
 		g_free(req);
 
@@ -564,6 +590,40 @@ static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
 	process_measurement(csc, pdu + 3, len - 3);
 }
 
+static void controlpoint_property_reply(struct controlpoint_req *req,
+								uint8_t code)
+{
+	switch (code) {
+	case RSP_SUCCESS:
+		g_dbus_pending_property_success(req->reply_id);
+		break;
+
+	case RSP_NOT_SUPPORTED:
+		g_dbus_pending_property_error(req->reply_id,
+					ERROR_INTERFACE ".NotSupported",
+					"Feature is not supported");
+		break;
+
+	case RSP_INVALID_PARAM:
+		g_dbus_pending_property_error(req->reply_id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		break;
+
+	case RSP_FAILED:
+		g_dbus_pending_property_error(req->reply_id,
+					ERROR_INTERFACE ".Failed",
+					"Operation failed");
+		break;
+
+	default:
+		g_dbus_pending_property_error(req->reply_id,
+					ERROR_INTERFACE ".Failed",
+					"Operation failed (%d)", code);
+		break;
+	}
+}
+
 static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
 							gpointer user_data)
 {
@@ -623,6 +683,16 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
 			error("Failed to read Supported Sendor Locations");
 		}
 		break;
+
+	case UPDATE_SENSOR_LOC:
+		csc->location = req->pending_location;
+
+		controlpoint_property_reply(req, rsp_code);
+
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					device_get_path(csc->dev),
+					CYCLINGSPEED_INTERFACE, "Location");
+		break;
 	}
 
 	csc->pending_req = NULL;
@@ -890,6 +960,56 @@ static gboolean property_get_location(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
+static void property_set_location(const GDBusPropertyTable *property,
+					DBusMessageIter *iter,
+					GDBusPendingPropertySet id, void *data)
+{
+	struct csc *csc = data;
+	char *loc;
+	int loc_val;
+	uint8_t att_val[2];
+	struct controlpoint_req *req;
+
+	if (csc->pending_req != NULL)
+		return g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InProgress",
+					"Operation already in progress");
+
+	if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT))
+		return g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".NotSupported",
+					"Feature is not supported");
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+
+	dbus_message_iter_get_basic(iter, &loc);
+
+	loc_val = str2location(loc);
+
+	if (loc_val < 0)
+		return g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+
+	req = g_new(struct controlpoint_req, 1);
+	req->csc = csc;
+	req->reply_id = id;
+	req->opcode = UPDATE_SENSOR_LOC;
+	req->pending_location = loc_val;
+
+	csc->pending_req = req;
+
+	att_val[0] = UPDATE_SENSOR_LOC;
+	att_val[1] = loc_val;
+
+	gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
+		sizeof(att_val), controlpoint_write_cb, req);
+
+}
+
 static gboolean property_exists_location(const GDBusPropertyTable *property,
 								void *data)
 {
@@ -954,7 +1074,7 @@ static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
 }
 
 static const GDBusPropertyTable cyclingspeed_device_properties[] = {
-	{ "Location", "s", property_get_location, NULL,
+	{ "Location", "s", property_get_location, property_set_location,
 						property_exists_location },
 	{ "SupportedLocations", "as", property_get_locations, NULL,
 						property_exists_locations },
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 14/20] cyclingspeed: Add support for Set Cumulative Value
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 profiles/cyclingspeed/cyclingspeed.c | 92 +++++++++++++++++++++++++++++++++++-
 1 file changed, 91 insertions(+), 1 deletion(-)

diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 58ccf74..8cc7028 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -76,6 +76,7 @@ struct controlpoint_req {
 	uint8_t			opcode;
 	guint			timeout;
 	GDBusPendingReply	reply_id;
+	DBusMessage		*msg;
 
 	uint8_t			pending_location;
 };
@@ -290,6 +291,15 @@ static gboolean controlpoint_timeout(gpointer user_data)
 		g_dbus_pending_property_error(req->reply_id,
 				ERROR_INTERFACE ".Failed",
 				"Operation failed (timeout)");
+	} else if (req->opcode == SET_CUMULATIVE_VALUE) {
+		DBusMessage *reply;
+
+		reply = btd_error_failed(req->msg,
+						"Operation failed (timeout)");
+
+		g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+		dbus_message_unref(req->msg);
 	}
 
 	req->csc->pending_req = NULL;
@@ -310,6 +320,15 @@ static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
 			g_dbus_pending_property_error(req->reply_id,
 					ERROR_INTERFACE ".Failed",
 					"Operation failed (%d)", status);
+		} else if  (req->opcode == SET_CUMULATIVE_VALUE) {
+			DBusMessage *reply;
+
+			reply = btd_error_failed(req->msg,
+						"Operation failed");
+
+			g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+			dbus_message_unref(req->msg);
 		}
 
 		req->csc->pending_req = NULL;
@@ -624,6 +643,34 @@ static void controlpoint_property_reply(struct controlpoint_req *req,
 	}
 }
 
+static void controlpoint_method_reply(struct controlpoint_req *req,
+								uint8_t code)
+{
+	DBusMessage *reply;
+
+	switch (code) {
+	case RSP_SUCCESS:
+		reply = dbus_message_new_method_return(req->msg);
+		break;
+	case RSP_NOT_SUPPORTED:
+		reply = btd_error_not_supported(req->msg);
+		break;
+	case RSP_INVALID_PARAM:
+		reply = btd_error_invalid_args(req->msg);
+		break;
+	case RSP_FAILED:
+		reply = btd_error_failed(req->msg, "Failed");
+		break;
+	default:
+		reply = btd_error_failed(req->msg, "Unknown error");
+		break;
+	}
+
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+	dbus_message_unref(req->msg);
+}
+
 static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
 							gpointer user_data)
 {
@@ -675,6 +722,10 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
 	}
 
 	switch (req->opcode) {
+	case SET_CUMULATIVE_VALUE:
+		controlpoint_method_reply(req, rsp_code);
+		break;
+
 	case REQUEST_SUPPORTED_SENSOR_LOC:
 		if (rsp_code == RSP_SUCCESS) {
 			csc->num_locations = len;
@@ -1083,6 +1134,44 @@ static const GDBusPropertyTable cyclingspeed_device_properties[] = {
 	{ }
 };
 
+static DBusMessage *set_cumulative_wheel_rev(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct csc *csc = data;
+	dbus_uint32_t value;
+	struct controlpoint_req *req;
+	uint8_t att_val[5]; /* uint8 opcode + uint32 value */
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &value,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	if (csc->pending_req != NULL)
+		return btd_error_in_progress(msg);
+
+	req = g_new(struct controlpoint_req, 1);
+	req->csc = csc;
+	req->opcode = SET_CUMULATIVE_VALUE;
+	req->msg = dbus_message_ref(msg);
+
+	csc->pending_req = req;
+
+	att_val[0] = SET_CUMULATIVE_VALUE;
+	att_put_u32(value, att_val + 1);
+
+	gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
+		sizeof(att_val), controlpoint_write_cb, req);
+
+	return NULL;
+}
+
+static const GDBusMethodTable cyclingspeed_device_methods[] = {
+	{ GDBUS_ASYNC_METHOD("SetCumulativeWheelRevolutions",
+			GDBUS_ARGS({ "value", "u" }), NULL,
+			set_cumulative_wheel_rev) },
+	{ }
+};
+
 static gint cmp_primary_uuid(gconstpointer a, gconstpointer b)
 {
 	const struct gatt_primary *prim = a;
@@ -1122,7 +1211,8 @@ static int csc_device_probe(struct btd_profile *p,
 	if (!g_dbus_register_interface(btd_get_dbus_connection(),
 						device_get_path(device),
 						CYCLINGSPEED_INTERFACE,
-						NULL, NULL,
+						cyclingspeed_device_methods,
+						NULL,
 						cyclingspeed_device_properties,
 						csc, destroy_csc)) {
 		error("D-Bus failed to register %s interface",
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 15/20] core: Add CyclingSpeedWatcher interface to default policy
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 src/bluetooth.conf | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/bluetooth.conf b/src/bluetooth.conf
index bd48cf9..4d0d504 100644
--- a/src/bluetooth.conf
+++ b/src/bluetooth.conf
@@ -19,6 +19,7 @@
     <allow send_interface="org.bluez.AlertAgent"/>
     <allow send_interface="org.bluez.Profile1"/>
     <allow send_interface="org.bluez.HeartRateWatcher"/>
+    <allow send_interface="org.bluez.CyclingSpeedWatcher"/>
   </policy>
 
   <policy at_console="true">
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 16/20] doc: Remove Get-/SetProperties from CSC API document
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

cyclingspeed plugin uses DBus.Properties instead of custom methods.
---
 doc/cycling-api.txt | 20 +-------------------
 1 file changed, 1 insertion(+), 19 deletions(-)

diff --git a/doc/cycling-api.txt b/doc/cycling-api.txt
index adbcd33..08e11c8 100644
--- a/doc/cycling-api.txt
+++ b/doc/cycling-api.txt
@@ -28,31 +28,13 @@ Service		org.bluez
 Interface	org.bluez.CyclingSpeed
 Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
 
-Methods		void SetProperty(string name, variant value)
-
-			Changes the value of the specified property. Only
-			read-write properties can be changed. On success
-			this will emit a PropertyChanged signal.
-
-			Possible Errors: org.bluez.Error.InvalidArguments
-
-		dict GetProperties()
-
-			Returns all properties for the interface. See the
-			Properties section for the available properties.
-
-		SetCumulativeWheelRevolutions(uint32 value)
+Methods		SetCumulativeWheelRevolutions(uint32 value)
 
 			Sets cumulative wheel revolutions value if
 			Cumulative Wheel Revolutions feature is supported.
 
 			Possible Errors: org.bluez.Error.NotSupported
 
-Signals		PropertyChanged(string name, variant value)
-
-			This signal indicates a changed value of the given
-			property.
-
 Properties	string Location (optional) [readwrite]
 
 			Current sensor location, if supported.
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 17/20] doc: Rename cycling API to cyclingspeed
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 doc/cycling-api.txt      | 100 -----------------------------------------------
 doc/cyclingspeed-api.txt | 100 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 100 insertions(+), 100 deletions(-)
 delete mode 100644 doc/cycling-api.txt
 create mode 100644 doc/cyclingspeed-api.txt

diff --git a/doc/cycling-api.txt b/doc/cycling-api.txt
deleted file mode 100644
index 08e11c8..0000000
--- a/doc/cycling-api.txt
+++ /dev/null
@@ -1,100 +0,0 @@
-Cycling Speed and Cadence API description
-*****************************************
-
-Copyright (C) 2012	Tieto Poland
-
-Cycling Speed and Cadence Manager hierarchy
-===========================================
-
-Service		org.bluez
-Interface	org.bluez.CyclingSpeedManager
-Object path	[variable prefix]/{hci0,hci1,...}
-
-Methods		RegisterWatcher(object agent)
-
-			Registers a watcher to monitor cycling speed and
-			cadence measurements.
-
-			Possible Errors: org.bluez.Error.InvalidArguments
-
-		UnregisterWatcher(object agent)
-
-			Unregisters a watcher.
-
-Cycling Speed and Cadence Profile hierarchy
-===========================================
-
-Service		org.bluez
-Interface	org.bluez.CyclingSpeed
-Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
-
-Methods		SetCumulativeWheelRevolutions(uint32 value)
-
-			Sets cumulative wheel revolutions value if
-			Cumulative Wheel Revolutions feature is supported.
-
-			Possible Errors: org.bluez.Error.NotSupported
-
-Properties	string Location (optional) [readwrite]
-
-			Current sensor location, if supported.
-			If Multiple Sensor Locations feature is supported,
-			this property can be set to one of values read from
-			SupportedLocations property.
-
-			Possible values: "other", "top-of-shoe", "in-shoe",
-					"hip", "front-wheel", "left-crank",
-					"right-crank", "left-pedal",
-					"right-pedal", "front-hub",
-					"rear-dropout", "chainstay",
-					"rear-wheel", "rear-hub"
-
-		array{string} SupportedLocations (optional) [readonly]
-
-			List of locations supported by sensor, only present
-			if Multiple Sensor Locations feature is supported.
-
-		boolean WheelRevolutionDataSupported [readonly]
-
-			true if sensor can read and set Cumulative Wheel
-			Revolutions value, false otherwise.
-
-		boolean MultipleSensorLocationsSupported [readonly]
-
-			true if sensor supports Multiple Sensor Locations
-			feature and can set Location, false otherwise.
-
-Cycling Speed and Cadence Watcher hierarchy
-===========================================
-
-Service		unique name
-Interface	org.bluez.CyclingSpeedWatcher
-Object path	freely definable
-
-Methods		void MeasurementReceived(object device, dict measurement)
-
-			This callback is called whenever wheel and/or crank
-			revolutions measurement is received from sensor.
-
-			Measurement:
-
-				uint32 WheelRevolutions (optional):
-
-					Cumulative number of wheel revolutions.
-
-				uint16 LastWheelEventTime (optional):
-
-					Time of last event from wheel sensor.
-					Value is expressed in 1/1024 second
-					units and can roll over during a ride.
-
-				uint16 CrankRevolutions (optional):
-
-					Cumulative number of crank revolutions.
-					This value can occasionally roll over.
-
-				uint16 LastCrankEventTime (optional):
-
-					Time of last event from crank sensor.
-					Value is expressed in 1/1024 second
-					units and can roll over during a ride.
diff --git a/doc/cyclingspeed-api.txt b/doc/cyclingspeed-api.txt
new file mode 100644
index 0000000..08e11c8
--- /dev/null
+++ b/doc/cyclingspeed-api.txt
@@ -0,0 +1,100 @@
+Cycling Speed and Cadence API description
+*****************************************
+
+Copyright (C) 2012	Tieto Poland
+
+Cycling Speed and Cadence Manager hierarchy
+===========================================
+
+Service		org.bluez
+Interface	org.bluez.CyclingSpeedManager
+Object path	[variable prefix]/{hci0,hci1,...}
+
+Methods		RegisterWatcher(object agent)
+
+			Registers a watcher to monitor cycling speed and
+			cadence measurements.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+
+		UnregisterWatcher(object agent)
+
+			Unregisters a watcher.
+
+Cycling Speed and Cadence Profile hierarchy
+===========================================
+
+Service		org.bluez
+Interface	org.bluez.CyclingSpeed
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods		SetCumulativeWheelRevolutions(uint32 value)
+
+			Sets cumulative wheel revolutions value if
+			Cumulative Wheel Revolutions feature is supported.
+
+			Possible Errors: org.bluez.Error.NotSupported
+
+Properties	string Location (optional) [readwrite]
+
+			Current sensor location, if supported.
+			If Multiple Sensor Locations feature is supported,
+			this property can be set to one of values read from
+			SupportedLocations property.
+
+			Possible values: "other", "top-of-shoe", "in-shoe",
+					"hip", "front-wheel", "left-crank",
+					"right-crank", "left-pedal",
+					"right-pedal", "front-hub",
+					"rear-dropout", "chainstay",
+					"rear-wheel", "rear-hub"
+
+		array{string} SupportedLocations (optional) [readonly]
+
+			List of locations supported by sensor, only present
+			if Multiple Sensor Locations feature is supported.
+
+		boolean WheelRevolutionDataSupported [readonly]
+
+			true if sensor can read and set Cumulative Wheel
+			Revolutions value, false otherwise.
+
+		boolean MultipleSensorLocationsSupported [readonly]
+
+			true if sensor supports Multiple Sensor Locations
+			feature and can set Location, false otherwise.
+
+Cycling Speed and Cadence Watcher hierarchy
+===========================================
+
+Service		unique name
+Interface	org.bluez.CyclingSpeedWatcher
+Object path	freely definable
+
+Methods		void MeasurementReceived(object device, dict measurement)
+
+			This callback is called whenever wheel and/or crank
+			revolutions measurement is received from sensor.
+
+			Measurement:
+
+				uint32 WheelRevolutions (optional):
+
+					Cumulative number of wheel revolutions.
+
+				uint16 LastWheelEventTime (optional):
+
+					Time of last event from wheel sensor.
+					Value is expressed in 1/1024 second
+					units and can roll over during a ride.
+
+				uint16 CrankRevolutions (optional):
+
+					Cumulative number of crank revolutions.
+					This value can occasionally roll over.
+
+				uint16 LastCrankEventTime (optional):
+
+					Time of last event from crank sensor.
+					Value is expressed in 1/1024 second
+					units and can roll over during a ride.
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 18/20] build: Add CSCP API document to EXTRA_DIST
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 Makefile.am | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Makefile.am b/Makefile.am
index d009828..2e0e9b2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -342,7 +342,8 @@ EXTRA_DIST += doc/manager-api.txt \
 		doc/sap-api.txt doc/media-api.txt doc/assigned-numbers.txt \
 		doc/supported-features.txt doc/alert-api.txt doc/mgmt-api.txt \
 		doc/oob-api.txt doc/proximity-api.txt doc/heartrate-api.txt \
-		doc/thermometer-api.txt doc/audio-telephony-design.txt
+		doc/thermometer-api.txt doc/audio-telephony-design.txt \
+		doc/cyclingspeed-api.txt
 
 AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@
 
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 19/20] test: Add cyclingspeed test script
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

---
 Makefile.tools         |   3 +-
 test/test-cyclingspeed | 124 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 126 insertions(+), 1 deletion(-)
 create mode 100755 test/test-cyclingspeed

diff --git a/Makefile.tools b/Makefile.tools
index 689b4d2..ded9faf 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -209,4 +209,5 @@ EXTRA_DIST += test/sap_client.py test/hsplay test/hsmicro \
 		test/test-health-sink test/service-record.dtd \
 		test/service-did.xml test/service-spp.xml test/service-opp.xml \
 		test/service-ftp.xml test/simple-player test/test-nap \
-		test/test-heartrate test/test-alert test/test-hfp
+		test/test-heartrate test/test-alert test/test-hfp \
+		test/test-cycling
diff --git a/test/test-cyclingspeed b/test/test-cyclingspeed
new file mode 100755
index 0000000..92fe935
--- /dev/null
+++ b/test/test-cyclingspeed
@@ -0,0 +1,124 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+'''
+Cycling Speed and Cadence test script
+'''
+
+import gobject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+
+class Watcher(dbus.service.Object):
+	@dbus.service.method("org.bluez.CyclingSpeedWatcher",
+					in_signature="oa{sv}", out_signature="")
+	def MeasurementReceived(self, device, measure):
+		print("Measurement received from %s" % device)
+
+		if "WheelRevolutions" in measure:
+			print("WheelRevolutions: ", measure["WheelRevolutions"])
+
+		if "LastWheelEventTime" in measure:
+			print("LastWheelEventTime: ", measure["LastWheelEventTime"])
+
+		if "CrankRevolutions" in measure:
+			print("CrankRevolutions: ", measure["CrankRevolutions"])
+
+		if "LastCrankEventTime" in measure:
+			print("LastCrankEventTime: ", measure["LastCrankEventTime"])
+
+def properties_changed(interface, changed, invalidated):
+	if "Location" in changed:
+		print("Sensor location: %s" % changed["Location"])
+
+if __name__ == "__main__":
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	option_list = [
+		make_option("-i", "--adapter", action="store",
+			type="string", dest="adapter"),
+		make_option("-b", "--device", action="store",
+			type="string", dest="address"),
+		]
+
+	parser = OptionParser(option_list=option_list)
+
+	(options, args) = parser.parse_args()
+
+	if not options.address:
+		print("Usage: %s [-i <adapter>] -b <bdaddr> [-c <value>] [cmd]" % (sys.argv[0]))
+		print("Possible commands:")
+		print("\tShowSupportedLocations")
+		print("\tSetLocation <location>")
+		print("\tSetCumulativeWheelRevolutions <value>")
+		sys.exit(1)
+
+	manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+					"org.bluez.Manager")
+	if options.adapter:
+		adapter_path = manager.FindAdapter(options.adapter)
+	else:
+		adapter_path = manager.DefaultAdapter()
+
+	adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+							"org.bluez.Adapter")
+
+	device_path = adapter.FindDevice(options.address)
+
+	cscmanager = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+						"org.bluez.CyclingSpeedManager")
+
+	watcher_path = "/test/watcher"
+	watcher = Watcher(bus, watcher_path)
+	cscmanager.RegisterWatcher(watcher_path)
+
+	csc = dbus.Interface(bus.get_object("org.bluez", device_path),
+						"org.bluez.CyclingSpeed")
+
+	bus.add_signal_receiver(properties_changed, bus_name="org.bluez",
+				path=device_path,
+				dbus_interface="org.freedesktop.DBus.Properties",
+				signal_name="PropertiesChanged")
+
+	device_prop = dbus.Interface(bus.get_object("org.bluez", device_path),
+					"org.freedesktop.DBus.Properties")
+
+	properties = device_prop.GetAll("org.bluez.CyclingSpeed")
+
+	if "Location" in properties:
+		print("Sensor location: %s" % properties["Location"])
+	else:
+		print("Sensor location is not supported")
+
+	if len(args) > 0:
+		if args[0] == "ShowSupportedLocations":
+			if properties["MultipleSensorLocationsSupported"]:
+				print("Supported locations: ", properties["SupportedLocations"])
+			else:
+				print("Multiple sensor locations not supported")
+
+		elif args[0] == "SetLocation":
+			if properties["MultipleSensorLocationsSupported"]:
+				device_prop.Set("org.bluez.CyclingSpeed", "Location", args[1])
+			else:
+				print("Multiple sensor locations not supported")
+
+		elif args[0] == "SetCumulativeWheelRevolutions":
+			if properties["WheelRevolutionDataSupported"]:
+				csc.SetCumulativeWheelRevolutions(dbus.UInt32(args[1]))
+			else:
+				print("Wheel revolution data not supported")
+
+		else:
+			print("Unknown command")
+			sys.exit(1)
+
+	mainloop = gobject.MainLoop()
+	mainloop.run()
-- 
1.8.0


^ permalink raw reply related

* [PATCH v3 20/20] test: Enable speed and cadence calculation in test-cyclingspeed
From: Andrzej Kaczmarek @ 2012-12-04 12:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1354625026-29358-1-git-send-email-andrzej.kaczmarek@tieto.com>

This patch allows to enable instantenous speed and cadence calculation
based on measurement received from remote device.

To enable calculations additional parameter "--circumference <value>"
needs to be used when calling script which sets wheel circumference in mm.

Both speed and cadence are enabled at the same time. Speed is calculated
in km/h and cadence in rpm.
---
 test/test-cyclingspeed | 71 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 69 insertions(+), 2 deletions(-)

diff --git a/test/test-cyclingspeed b/test/test-cyclingspeed
index 92fe935..841456c 100755
--- a/test/test-cyclingspeed
+++ b/test/test-cyclingspeed
@@ -14,23 +14,86 @@ import dbus.service
 import dbus.mainloop.glib
 from optparse import OptionParser, make_option
 
+class MeasurementQ:
+	def __init__(self, wrap_v):
+		self._now = [None, None]
+		self._prev = [None, None]
+		self._wrap_v = wrap_v
+
+	def can_calc(self):
+		return ((self._now[0] is not None)
+			and (self._now[1] is not None)
+			and (self._prev[0] is not None)
+			and (self._prev[1] is not None))
+
+	def delta_v(self):
+		delta = self._now[0] - self._prev[0]
+		if (delta < 0) and (self._wrap_v):
+			delta = delta + 65536
+		return delta
+
+	def delta_t(self):
+		delta = self._now[1] - self._prev[1]
+		if delta < 0:
+			delta = delta + 65536
+		return delta
+
+	def put(self, data):
+		self._prev = self._now
+		self._now = data
+
 class Watcher(dbus.service.Object):
+	_wheel = MeasurementQ(False)
+	_crank = MeasurementQ(True)
+	_circumference = None
+
+	def enable_calc(self, v):
+		self._circumference = v
+
 	@dbus.service.method("org.bluez.CyclingSpeedWatcher",
 					in_signature="oa{sv}", out_signature="")
 	def MeasurementReceived(self, device, measure):
 		print("Measurement received from %s" % device)
 
+		rev = None
+		evt = None
 		if "WheelRevolutions" in measure:
+			rev = measure["WheelRevolutions"]
 			print("WheelRevolutions: ", measure["WheelRevolutions"])
-
 		if "LastWheelEventTime" in measure:
+			evt = measure["LastWheelEventTime"]
 			print("LastWheelEventTime: ", measure["LastWheelEventTime"])
+		self._wheel.put( [rev, evt] )
 
+		rev = None
+		evt = None
 		if "CrankRevolutions" in measure:
+			rev = measure["CrankRevolutions"]
 			print("CrankRevolutions: ", measure["CrankRevolutions"])
-
 		if "LastCrankEventTime" in measure:
+			evt = measure["LastCrankEventTime"]
 			print("LastCrankEventTime: ", measure["LastCrankEventTime"])
+		self._crank.put( [rev, evt] )
+
+		if self._circumference is None:
+			return
+
+		if self._wheel.can_calc():
+			delta_v = self._wheel.delta_v()
+			delta_t = self._wheel.delta_t()
+
+			if (delta_v >= 0) and (delta_t > 0):
+				speed = delta_v * self._circumference * 1024 / delta_t # mm/s
+				speed = speed * 0.0036 # mm/s -> km/h
+				print("(calculated) Speed: %.2f km/h" % speed)
+
+		if self._crank.can_calc():
+			delta_v = self._crank.delta_v()
+			delta_t = self._crank.delta_t()
+
+			if delta_t > 0:
+				cadence = delta_v * 1024 / delta_t
+				print("(calculated) Cadence: %d rpm" % cadence)
 
 def properties_changed(interface, changed, invalidated):
 	if "Location" in changed:
@@ -46,6 +109,8 @@ if __name__ == "__main__":
 			type="string", dest="adapter"),
 		make_option("-b", "--device", action="store",
 			type="string", dest="address"),
+		make_option("-c", "--circumference", action="store",
+			type="int", dest="circumference"),
 		]
 
 	parser = OptionParser(option_list=option_list)
@@ -77,6 +142,8 @@ if __name__ == "__main__":
 
 	watcher_path = "/test/watcher"
 	watcher = Watcher(bus, watcher_path)
+	if options.circumference:
+		watcher.enable_calc(options.circumference)
 	cscmanager.RegisterWatcher(watcher_path)
 
 	csc = dbus.Interface(bus.get_object("org.bluez", device_path),
-- 
1.8.0


^ permalink raw reply related

* Bluez 4.99 - Connection/Authentification keeps pending
From: Mario Premke @ 2012-12-04 13:50 UTC (permalink / raw)
  To: linux-bluetooth

Hello,
I am running: Bluez-4.99 ; Kernel 3.6.8; connecting a mouse (Logitech V470) 
with simple-agent was no problem.
Trying (for hours) to connect a Keysonic ACK-595BT keyboard - to no avail.

The pin somehow gets exchanged, but then it gets stuck:

... simple-agent hci0 00:18:00:00:D0:88
RequestPinCode (/org/bluez/1644/hci0/dev_00_18_00_00_D0_88)
Enter PIN Code: 123456
Release exit: False <- this is my debug print "Release exit: %s" % 
self.exit_on_release in simple-agent
Creating device failed: org.freedesktop.DBus.Error.NoReply: Did not receive 
a reply. Possible causes include: the remote application did not send a 
reply, the message bus security policy blocked the reply, the reply timeout 
expired, or the network connection was broken.

and the connection/authentification keeps pending (snip from hcidump):

< HCI Command: Create Connection (0x01|0x0005) plen 13
    bdaddr 00:18:00:00:D0:88 ptype 0xcc18 rswitch 0x01 clkoffset 0x0000
    Packet type: DM1 DM3 DM5 DH1 DH3 DH5
> HCI Event: Command Status (0x0f) plen 4
    Create Connection (0x01|0x0005) status 0x00 ncmd 1
> HCI Event: Connect Complete (0x03) plen 11
    status 0x00 handle 37 bdaddr 00:18:00:00:D0:88 type ACL encrypt 0x00
< HCI Command: Read Remote Supported Features (0x01|0x001b) plen 2
    handle 37
> HCI Event: Command Status (0x0f) plen 4
    Read Remote Supported Features (0x01|0x001b) status 0x00 ncmd 0
> HCI Event: Page Scan Repetition Mode Change (0x20) plen 7
    bdaddr 00:18:00:00:D0:88 mode 1
> HCI Event: Max Slots Change (0x1b) plen 3
    handle 37 slots 5
> HCI Event: Command Status (0x0f) plen 4
    Unknown (0x00|0x0000) status 0x00 ncmd 1
> HCI Event: Read Remote Supported Features (0x0b) plen 11
    status 0x00 handle 37
    Features: 0xff 0xff 0x8f 0xfe 0x9b 0xf9 0x00 0x80
< HCI Command: Remote Name Request (0x01|0x0019) plen 10
    bdaddr 00:18:00:00:D0:88 mode 2 clkoffset 0x0000
> HCI Event: Command Status (0x0f) plen 4
    Remote Name Request (0x01|0x0019) status 0x00 ncmd 1
> HCI Event: Remote Name Req Complete (0x07) plen 255
    status 0x00 bdaddr 00:18:00:00:D0:88 name 'BTKB-D088'
< HCI Command: Authentication Requested (0x01|0x0011) plen 2
    handle 37
> HCI Event: Command Status (0x0f) plen 4
    Authentication Requested (0x01|0x0011) status 0x00 ncmd 1
> HCI Event: Link Key Request (0x17) plen 6
    bdaddr 00:18:00:00:D0:88
< HCI Command: Link Key Request Negative Reply (0x01|0x000c) plen 6
    bdaddr 00:18:00:00:D0:88
> HCI Event: Command Complete (0x0e) plen 10
    Link Key Request Negative Reply (0x01|0x000c) ncmd 1
    status 0x00 bdaddr 00:18:00:00:D0:88
> HCI Event: PIN Code Request (0x16) plen 6
    bdaddr 00:18:00:00:D0:88
< HCI Command: PIN Code Request Reply (0x01|0x000d) plen 23
    bdaddr 00:18:00:00:D0:88 len 6 pin '123456'
> HCI Event: Command Complete (0x0e) plen 10
    PIN Code Request Reply (0x01|0x000d) ncmd 1
    status 0x00 bdaddr 00:18:00:00:D0:88
> HCI Event: Link Key Notification (0x18) plen 23
    bdaddr 00:18:00:00:D0:88 key 86B9BE88F30AF1481F03C2D69894AD4C type 0
    Type: Combination Key
> HCI Event: Auth Complete (0x06) plen 3
    status 0x00 handle 37
< HCI Command: Authentication Requested (0x01|0x0011) plen 2
    handle 37
< ACL data: handle 37 flags 0x00 dlen 10
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> HCI Event: Command Status (0x0f) plen 4
    Authentication Requested (0x01|0x0011) status 0x00 ncmd 1
> HCI Event: Link Key Request (0x17) plen 6
    bdaddr 00:18:00:00:D0:88
< HCI Command: Link Key Request Reply (0x01|0x000b) plen 22
    bdaddr 00:18:00:00:D0:88 key 86B9BE88F30AF1481F03C2D69894AD4C
> HCI Event: Command Complete (0x0e) plen 10
    Link Key Request Reply (0x01|0x000b) ncmd 1
    status 0x00 bdaddr 00:18:00:00:D0:88
> ACL data: handle 37 flags 0x02 dlen 16
      Extended feature mask 0x0007
        Flow control mode
        Retransmission mode
        Bi-directional QoS
< ACL data: handle 37 flags 0x00 dlen 12
> HCI Event: Auth Complete (0x06) plen 3
    status 0x00 handle 37
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> ACL data: handle 37 flags 0x02 dlen 16
      Connection pending - Authorization pending
> ACL data: handle 37 flags 0x02 dlen 16
      Connection successful
< ACL data: handle 37 flags 0x00 dlen 12
> ACL data: handle 37 flags 0x02 dlen 12
< ACL data: handle 37 flags 0x00 dlen 18
      MTU 672
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> ACL data: handle 37 flags 0x02 dlen 14
      Success
< ACL data: handle 37 flags 0x00 dlen 24
        SDP SSA Req: tid 0x0 len 0xf
          max 65535
          aid(s) 0x0000 - 0xffff
          cont 00
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> HCI Event: Mode Change (0x14) plen 6
    Mode: Active
> HCI Event: Mode Change (0x14) plen 6
    Mode: Sniff
< ACL data: handle 37 flags 0x00 dlen 12
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> HCI Event: Disconn Complete (0x05) plen 4
    status 0x00 handle 37 reason 0x08
    Reason: Connection Timeout
< HCI Command: Delete Stored Link Key (0x03|0x0012) plen 7
    bdaddr 00:18:00:00:D0:88 all 0
> HCI Event: Command Complete (0x0e) plen 6
    Delete Stored Link Key (0x03|0x0012) ncmd 1
    status 0x00 deleted 0

When I then struck a key at the BT keyboard hcidump shows the following:

> HCI Event: Mode Change (0x14) plen 6
    Mode: Active
> HCI Event: Mode Change (0x14) plen 6
    Mode: Sniff
> HCI Event: Connect Request (0x04) plen 10
    bdaddr 00:18:00:00:D0:88 class 0x002540 type ACL
< HCI Command: Accept Connection Request (0x01|0x0009) plen 7
    bdaddr 00:18:00:00:D0:88 role 0x00
    Role: Master
> HCI Event: Command Status (0x0f) plen 4
    Accept Connection Request (0x01|0x0009) status 0x00 ncmd 1
> HCI Event: Role Change (0x12) plen 8
    status 0x00 bdaddr 00:18:00:00:D0:88 role 0x00
    Role: Master
> HCI Event: Connect Complete (0x03) plen 11
    status 0x00 handle 37 bdaddr 00:18:00:00:D0:88 type ACL encrypt 0x00
< HCI Command: Read Remote Supported Features (0x01|0x001b) plen 2
    handle 37
> HCI Event: Page Scan Repetition Mode Change (0x20) plen 7
    bdaddr 00:18:00:00:D0:88 mode 1
> HCI Event: Command Status (0x0f) plen 4
    Read Remote Supported Features (0x01|0x001b) status 0x00 ncmd 0
> HCI Event: Max Slots Change (0x1b) plen 3
    handle 37 slots 5
> HCI Event: Command Status (0x0f) plen 4
    Unknown (0x00|0x0000) status 0x00 ncmd 1
> ACL data: handle 37 flags 0x02 dlen 12
< ACL data: handle 37 flags 0x00 dlen 16
      Connection pending - No futher information available
< ACL data: handle 37 flags 0x00 dlen 10
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> HCI Event: Read Remote Supported Features (0x0b) plen 11
    status 0x00 handle 37
    Features: 0xff 0xff 0x8f 0xfe 0x9b 0xf9 0x00 0x80
< HCI Command: Remote Name Request (0x01|0x0019) plen 10
    bdaddr 00:18:00:00:D0:88 mode 2 clkoffset 0x0000
> HCI Event: Command Status (0x0f) plen 4
    Remote Name Request (0x01|0x0019) status 0x00 ncmd 1
> ACL data: handle 37 flags 0x02 dlen 16
      Extended feature mask 0x0007
        Flow control mode
        Retransmission mode
        Bi-directional QoS
< ACL data: handle 37 flags 0x00 dlen 16
      Connection successful
< ACL data: handle 37 flags 0x00 dlen 12
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> ACL data: handle 37 flags 0x02 dlen 12
< ACL data: handle 37 flags 0x00 dlen 18
      MTU 672
> ACL data: handle 37 flags 0x02 dlen 14
      Success
< ACL data: handle 37 flags 0x00 dlen 5
      HIDP: Control: Virtual cable unplug
< ACL data: handle 37 flags 0x00 dlen 12
> HCI Event: Remote Name Req Complete (0x07) plen 255
    status 0x00 bdaddr 00:18:00:00:D0:88 name 'BTKB-D088'
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> ACL data: handle 37 flags 0x02 dlen 12
> ACL data: handle 37 flags 0x02 dlen 12
< ACL data: handle 37 flags 0x00 dlen 16
      Connection pending - Authorization pending
< ACL data: handle 37 flags 0x00 dlen 16
      Connection refused - security block
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 37 packets 1
> HCI Event: Disconn Complete (0x05) plen 4
    status 0x00 handle 37 reason 0x13
    Reason: Remote User Terminated Connection


This is what /var/log/messages says:

dbus[1631]: [system] Rejected send message, 1 matched rules; 
type="method_return", sender=":1.31" (uid=0 pid=3009 comm="/usr/bin/python2 
/usr/bin/simple-agent hci0 00:18:") interface="(unset)" member="(unset)" 
error name="(unset)" requested_reply="0" destination=":1.0" (uid=0 pid=1644 
comm="/usr/sbin/bluetoothd --udev ")

My policies at /etc/dbus-1/system.d/bluetooth.conf are:

<policy user="root">
<allow own="org.bluez"/>
<allow send_destination="org.bluez"/>
<allow send_interface="org.bluez.Agent"/>
<allow send_interface="org.bluez.HandsfreeAgent"/>
<allow send_interface="org.bluez.MediaEndpoint"/>
<allow send_interface="org.bluez.MediaPlayer"/>
<allow send_interface="org.bluez.Watcher"/>
<allow send_interface="org.bluez.ThermometerWatcher"/>

<allow send_type="method_call"/>
<allow send_type="method_return"/>
</policy>

I am fiddling around with the allowance for the send_types, but it always 
runs into the rejected message type as can be seen in the syslog, probably 
because of the "unset" interface (see syslog), since I have (not 
surprisingly) no rule for this interface.

So it seems to get stuck in the authentification process, but I have no clue 
why?
In Bluez-3.x there was a programm 'passkey-agent', is it obsolete that it's 
not included in Bluez-4.x any more?
I once connected the keyboard to another linux system, but that was with 
Bluez-3.x ...

Thanks in advance
Mario 


^ permalink raw reply

* Re: [PATCH BlueZ 2/2 v1] l2test: Add support to test auto select PSM
From: Syam Sidhardhan @ 2012-12-04 14:36 UTC (permalink / raw)
  To: Gustavo Padovan, Syam Sidhardhan, linux-bluetooth
In-Reply-To: <20121120175213.GH14006@joana>

Hi Gustavo,

On Tue, Nov 20, 2012 at 11:22 PM, Gustavo Padovan <gustavo@padovan.org> wrote:
> Hi Syam,
>
> * Syam Sidhardhan <s.syam@samsung.com> [2012-11-20 21:45:51 +0530]:
>
>> This patch enable us to test the auto select PSM for server by passing
>> PSM value as 0.
>>
>> Ex: l2test -d -P 0
>> l2test[2585]: Waiting for connection on psm 4097 ...
>> ---
>> v1-> Corrected the misplaced code
>>
>>  test/l2test.c |    4 +---
>>  1 files changed, 1 insertions(+), 3 deletions(-)
>>
>> diff --git a/test/l2test.c b/test/l2test.c
>> index 7645681..485887f 100644
>> --- a/test/l2test.c
>> +++ b/test/l2test.c
>> @@ -87,7 +87,7 @@ static long buffer_size = 2048;
>>
>>  /* Default addr and psm and cid */
>>  static bdaddr_t bdaddr;
>> -static unsigned short psm = 0x1011;
>> +static unsigned short psm = 0;
>
> Your patch makes the client side to always pass the psm option and I don't
> think this is a good approach. I think we need something smarter here to not
> break things.

True, I assumed that for initiating or listening a connection, the PSM
or CID is mandatory.
I think, we should not change the psm initialization
"static unsigned short psm = 0x1011;"  in the previous patch.
If the user want to test the auto select psm, then he should
explicitly pass the "-P 0" option in the argument.

I'll send an updated version.

Regards,
Syam.

^ permalink raw reply

* [PATCH v0 0/3] Remove access rights from MediaTransport
From: Mikel Astiz @ 2012-12-04 17:13 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Mikel Astiz

From: Mikel Astiz <mikel.astiz@bmw-carit.de>

The accessrights used in the Acquire() method are not very useful and can thus be removed. This simplifies both the D-Bus API and the internal implementation significantly.

Mikel Astiz (3):
  media: Remove transport access type from D-Bus API
  media: Remove internal transport locks
  media: Remove transport owner list

 doc/media-api.txt          |  30 ++----
 profiles/audio/transport.c | 238 ++++++++++-----------------------------------
 2 files changed, 59 insertions(+), 209 deletions(-)

-- 
1.7.11.7


^ permalink raw reply

* [PATCH v0 1/3] media: Remove transport access type from D-Bus API
From: Mikel Astiz @ 2012-12-04 17:13 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1354641230-3667-1-git-send-email-mikel.astiz.oss@gmail.com>

From: Mikel Astiz <mikel.astiz@bmw-carit.de>

There is no known use-case making use of these access types and
therefore the Media API can be simplified.

>From now on, the transport will always be acquired with read and write
access rights.
---
 doc/media-api.txt          | 30 ++++++++----------------------
 profiles/audio/transport.c | 44 +++++++++-----------------------------------
 2 files changed, 17 insertions(+), 57 deletions(-)

diff --git a/doc/media-api.txt b/doc/media-api.txt
index cf69efa..cb5d9a3 100644
--- a/doc/media-api.txt
+++ b/doc/media-api.txt
@@ -261,31 +261,17 @@ Service		org.bluez
 Interface	org.bluez.MediaTransport
 Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/fdX
 
-Methods		fd, uint16, uint16 Acquire(string accesstype)
+Methods		fd, uint16, uint16 Acquire(boolean optional)
 
-			Acquire transport file descriptor and the MTU for read
-			and write respectively.
+			Acquire transport (with read and write rights) and
+			return the file descriptor and the MTU.
 
-			possible accesstype:
+			The optional flag specifies that the transport will be
+			acquired only if it's in "pending" state, and will
+			otherwise fail. This means no request will be sent to
+			the remote side.
 
-				"r" : Read only access
-
-				"w" : Write only access
-
-				"rw": Read and write access
-
-			The accesstype string can also be combined with a "?"
-			suffix, which will make the request optional. This
-			typically means the transport will only be acquired if
-			it is already available (remote-initiated), but
-			otherwise no request will be sent to the remote side.
-			In this last case the function will fail. Note that,
-			due to compatibility issues with older versions of
-			BlueZ, clients are encouraged to use exactly the same
-			accesstype for Release(), matching the string provided
-			to Acquire().
-
-		void Release(string accesstype)
+		void Release()
 
 			Releases file descriptor.
 
diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index 610aca3..5924f73 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -127,19 +127,6 @@ static const char *lock2str(transport_lock_t lock)
 		return "rw";
 }
 
-static transport_lock_t str2lock(const char *str)
-{
-	transport_lock_t lock = 0;
-
-	if (g_strstr_len(str, -1, "r") != NULL)
-		lock |= TRANSPORT_LOCK_READ;
-
-	if (g_strstr_len(str, -1, "w") != NULL)
-		lock |= TRANSPORT_LOCK_WRITE;
-
-	return lock;
-}
-
 static const char *state2str(transport_state_t state)
 {
 	switch (state) {
@@ -532,12 +519,13 @@ static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
 	struct media_transport *transport = data;
 	struct media_owner *owner;
 	struct media_request *req;
-	const char *accesstype, *sender;
-	transport_lock_t lock;
+	const char *sender;
+	dbus_bool_t optional;
 	guint id;
+	transport_lock_t lock = TRANSPORT_LOCK_READ | TRANSPORT_LOCK_WRITE;
 
 	if (!dbus_message_get_args(msg, NULL,
-				DBUS_TYPE_STRING, &accesstype,
+				DBUS_TYPE_BOOLEAN, &optional,
 				DBUS_TYPE_INVALID))
 		return btd_error_invalid_args(msg);
 
@@ -547,12 +535,7 @@ static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
 	if (owner != NULL)
 		return btd_error_not_authorized(msg);
 
-	lock = str2lock(accesstype);
-	if (lock == 0)
-		return btd_error_invalid_args(msg);
-
-	if (transport->state != TRANSPORT_STATE_PENDING &&
-				g_strstr_len(accesstype, -1, "?") != NULL)
+	if (transport->state != TRANSPORT_STATE_PENDING && optional)
 		return btd_error_failed(msg, "Transport not playing");
 
 	if (media_transport_acquire(transport, lock) == FALSE)
@@ -578,14 +561,9 @@ static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
 {
 	struct media_transport *transport = data;
 	struct media_owner *owner;
-	const char *accesstype, *sender;
-	transport_lock_t lock;
+	const char *sender;
 	struct media_request *req;
-
-	if (!dbus_message_get_args(msg, NULL,
-				DBUS_TYPE_STRING, &accesstype,
-				DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
+	transport_lock_t lock = TRANSPORT_LOCK_READ | TRANSPORT_LOCK_WRITE;
 
 	sender = dbus_message_get_sender(msg);
 
@@ -593,8 +571,6 @@ static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
 	if (owner == NULL)
 		return btd_error_not_authorized(msg);
 
-	lock = str2lock(accesstype);
-
 	if (owner->lock == lock) {
 		guint id;
 
@@ -764,13 +740,11 @@ static void set_volume(const GDBusPropertyTable *property,
 
 static const GDBusMethodTable transport_methods[] = {
 	{ GDBUS_ASYNC_METHOD("Acquire",
-			GDBUS_ARGS({ "access_type", "s" }),
+			GDBUS_ARGS({ "optional", "b" }),
 			GDBUS_ARGS({ "fd", "h" }, { "mtu_r", "q" },
 							{ "mtu_w", "q" } ),
 			acquire) },
-	{ GDBUS_ASYNC_METHOD("Release",
-			GDBUS_ARGS({ "access_type", "s" }), NULL,
-			release ) },
+	{ GDBUS_ASYNC_METHOD("Release", NULL, NULL, release) },
 	{ },
 };
 
-- 
1.7.11.7


^ permalink raw reply related

* [PATCH v0 2/3] media: Remove internal transport locks
From: Mikel Astiz @ 2012-12-04 17:13 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1354641230-3667-1-git-send-email-mikel.astiz.oss@gmail.com>

From: Mikel Astiz <mikel.astiz@bmw-carit.de>

The internal transport_lock_t is not needed any more since transports
are now always acquired with read and write permissions.
---
 profiles/audio/transport.c | 134 ++++++++++-----------------------------------
 1 file changed, 30 insertions(+), 104 deletions(-)

diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index 5924f73..ad9270e 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -51,11 +51,6 @@
 #define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport"
 
 typedef enum {
-	TRANSPORT_LOCK_READ = 1,
-	TRANSPORT_LOCK_WRITE = 1 << 1,
-} transport_lock_t;
-
-typedef enum {
 	TRANSPORT_STATE_IDLE,		/* Not acquired and suspended */
 	TRANSPORT_STATE_PENDING,	/* Playing but not acquired */
 	TRANSPORT_STATE_REQUESTING,	/* Acquire in progress */
@@ -80,7 +75,6 @@ struct media_owner {
 	struct media_transport	*transport;
 	struct media_request	*pending;
 	char			*name;
-	transport_lock_t	lock;
 	guint			watch;
 };
 
@@ -100,7 +94,6 @@ struct media_transport {
 	int			fd;		/* Transport file descriptor */
 	uint16_t		imtu;		/* Transport input mtu */
 	uint16_t		omtu;		/* Transport output mtu */
-	transport_lock_t	lock;
 	transport_state_t	state;
 	guint			hs_watch;
 	guint			source_watch;
@@ -115,18 +108,6 @@ struct media_transport {
 	void			*data;
 };
 
-static const char *lock2str(transport_lock_t lock)
-{
-	if (lock == 0)
-		return "";
-	else if (lock == TRANSPORT_LOCK_READ)
-		return "r";
-	else if (lock == TRANSPORT_LOCK_WRITE)
-		return "w";
-	else
-		return "rw";
-}
-
 static const char *state2str(transport_state_t state)
 {
 	switch (state) {
@@ -229,20 +210,6 @@ static void media_request_reply(struct media_request *req, int err)
 	g_dbus_send_message(btd_get_dbus_connection(), reply);
 }
 
-static gboolean media_transport_release(struct media_transport *transport,
-							transport_lock_t lock)
-{
-	transport->lock &= ~lock;
-
-	if (lock & TRANSPORT_LOCK_READ)
-		DBG("Transport %s: read lock released", transport->path);
-
-	if (lock & TRANSPORT_LOCK_WRITE)
-		DBG("Transport %s: write lock released", transport->path);
-
-	return TRUE;
-}
-
 static void media_owner_remove(struct media_owner *owner)
 {
 	struct media_transport *transport = owner->transport;
@@ -279,8 +246,6 @@ static void media_transport_remove(struct media_transport *transport,
 {
 	DBG("Transport %s Owner %s", transport->path, owner->name);
 
-	media_transport_release(transport, owner->lock);
-
 	/* Reply if owner has a pending request */
 	if (owner->pending)
 		media_request_reply(owner->pending, EIO);
@@ -339,12 +304,6 @@ static void a2dp_resume_complete(struct avdtp *session,
 
 	media_transport_set_fd(transport, fd, imtu, omtu);
 
-	if ((owner->lock & TRANSPORT_LOCK_READ) == 0)
-		imtu = 0;
-
-	if ((owner->lock & TRANSPORT_LOCK_WRITE) == 0)
-		omtu = 0;
-
 	ret = g_dbus_send_reply(btd_get_dbus_connection(), req->msg,
 						DBUS_TYPE_UNIX_FD, &fd,
 						DBUS_TYPE_UINT16, &imtu,
@@ -443,24 +402,6 @@ static void media_owner_exit(DBusConnection *connection, void *user_data)
 	media_transport_remove(owner->transport, owner);
 }
 
-static gboolean media_transport_acquire(struct media_transport *transport,
-							transport_lock_t lock)
-{
-	if (transport->lock & lock)
-		return FALSE;
-
-	transport->lock |= lock;
-
-	if (lock & TRANSPORT_LOCK_READ)
-		DBG("Transport %s: read lock acquired", transport->path);
-
-	if (lock & TRANSPORT_LOCK_WRITE)
-		DBG("Transport %s: write lock acquired", transport->path);
-
-
-	return TRUE;
-}
-
 static void media_transport_add(struct media_transport *transport,
 					struct media_owner *owner)
 {
@@ -473,17 +414,14 @@ static void media_transport_add(struct media_transport *transport,
 							owner, NULL);
 }
 
-static struct media_owner *media_owner_create(DBusMessage *msg,
-						transport_lock_t lock)
+static struct media_owner *media_owner_create(DBusMessage *msg)
 {
 	struct media_owner *owner;
 
 	owner = g_new0(struct media_owner, 1);
 	owner->name = g_strdup(dbus_message_get_sender(msg));
-	owner->lock = lock;
 
-	DBG("Owner created: sender=%s accesstype=%s", owner->name,
-							lock2str(lock));
+	DBG("Owner created: sender=%s", owner->name);
 
 	return owner;
 }
@@ -522,7 +460,6 @@ static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
 	const char *sender;
 	dbus_bool_t optional;
 	guint id;
-	transport_lock_t lock = TRANSPORT_LOCK_READ | TRANSPORT_LOCK_WRITE;
 
 	if (!dbus_message_get_args(msg, NULL,
 				DBUS_TYPE_BOOLEAN, &optional,
@@ -535,16 +472,15 @@ static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
 	if (owner != NULL)
 		return btd_error_not_authorized(msg);
 
+	if (transport->state >= TRANSPORT_STATE_REQUESTING)
+		return btd_error_not_authorized(msg);
+
 	if (transport->state != TRANSPORT_STATE_PENDING && optional)
 		return btd_error_failed(msg, "Transport not playing");
 
-	if (media_transport_acquire(transport, lock) == FALSE)
-		return btd_error_not_authorized(msg);
-
-	owner = media_owner_create(msg, lock);
+	owner = media_owner_create(msg);
 	id = transport->resume(transport, owner);
 	if (id == 0) {
-		media_transport_release(transport, lock);
 		media_owner_free(owner);
 		return btd_error_not_authorized(msg);
 	}
@@ -563,7 +499,7 @@ static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
 	struct media_owner *owner;
 	const char *sender;
 	struct media_request *req;
-	transport_lock_t lock = TRANSPORT_LOCK_READ | TRANSPORT_LOCK_WRITE;
+	guint id;
 
 	sender = dbus_message_get_sender(msg);
 
@@ -571,45 +507,35 @@ static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
 	if (owner == NULL)
 		return btd_error_not_authorized(msg);
 
-	if (owner->lock == lock) {
-		guint id;
-
-		/* Not the last owner, no need to suspend */
-		if (g_slist_length(transport->owners) != 1) {
-			media_transport_remove(transport, owner);
-			return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-		}
-
-		if (owner->pending) {
-			const char *member;
+	/* Not the last owner, no need to suspend */
+	if (g_slist_length(transport->owners) != 1) {
+		media_transport_remove(transport, owner);
+		return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+	}
 
-			member = dbus_message_get_member(owner->pending->msg);
-			/* Cancel Acquire request if that exist */
-			if (g_str_equal(member, "Acquire"))
-				media_owner_remove(owner);
-			else
-				return btd_error_in_progress(msg);
-		}
+	if (owner->pending) {
+		const char *member;
 
-		transport_set_state(transport, TRANSPORT_STATE_SUSPENDING);
+		member = dbus_message_get_member(owner->pending->msg);
+		/* Cancel Acquire request if that exist */
+		if (g_str_equal(member, "Acquire"))
+			media_owner_remove(owner);
+		else
+			return btd_error_in_progress(msg);
+	}
 
-		id = transport->suspend(transport, owner);
-		if (id == 0) {
-			media_transport_remove(transport, owner);
-			return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-		}
+	transport_set_state(transport, TRANSPORT_STATE_SUSPENDING);
 
-		req = media_request_create(msg, id);
-		media_owner_add(owner, req);
+	id = transport->suspend(transport, owner);
+	if (id == 0) {
+		media_transport_remove(transport, owner);
+		return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+	}
 
-		return NULL;
-	} else if ((owner->lock & lock) == lock) {
-		media_transport_release(transport, lock);
-		owner->lock &= ~lock;
-	} else
-		return btd_error_not_authorized(msg);
+	req = media_request_create(msg, id);
+	media_owner_add(owner, req);
 
-	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+	return NULL;
 }
 
 static gboolean get_device(const GDBusPropertyTable *property,
-- 
1.7.11.7


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox