Linux bluetooth development
 help / color / mirror / Atom feed
* [RFC 4/8] obexd: Add Message Notification Server (MNS)
From: Christian Fetzer @ 2013-02-06 14:45 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1360161922-6585-1-git-send-email-christian.fetzer@oss.bmw-carit.de>

From: Christian Fetzer <christian.fetzer@bmw-carit.de>

This implements the MAP Message Notification Server (MNS)
which is part of the MCE.
---
 Makefile.obexd            |   3 +
 obexd/client/map-event.h  |  42 ++++++
 obexd/client/mns.c        | 348 ++++++++++++++++++++++++++++++++++++++++++++++
 obexd/plugins/bluetooth.c |   2 +
 obexd/src/obexd.h         |   1 +
 src/profile.c             |  55 ++++++++
 6 files changed, 451 insertions(+)
 create mode 100644 obexd/client/map-event.h
 create mode 100644 obexd/client/mns.c

diff --git a/Makefile.obexd b/Makefile.obexd
index ae05ae9..5824e0a 100644
--- a/Makefile.obexd
+++ b/Makefile.obexd
@@ -48,6 +48,9 @@ obexd_builtin_sources += obexd/plugins/mas.c obexd/src/map_ap.h \
 				obexd/plugins/messages.h \
 				obexd/plugins/messages-dummy.c
 
+obexd_builtin_modules += mns
+obexd_builtin_sources += obexd/client/mns.c obexd/src/map_ap.h \
+				obexd/client/map-event.h
 
 libexec_PROGRAMS += obexd/src/obexd
 
diff --git a/obexd/client/map-event.h b/obexd/client/map-event.h
new file mode 100644
index 0000000..749f1e0
--- /dev/null
+++ b/obexd/client/map-event.h
@@ -0,0 +1,42 @@
+/*
+ *
+ *  OBEX
+ *
+ *  Copyright (C) 2013  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  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
+ *
+ */
+
+enum map_event_type {
+	MAP_ET_NEW_MESSAGE,
+	MAP_ET_DELIVERY_SUCCESS,
+	MAP_ET_SENDING_SUCCESS,
+	MAP_ET_DELIVERY_FAILURE,
+	MAP_ET_SENDING_FAILURE,
+	MAP_ET_MEMORY_FULL,
+	MAP_ET_MEMORY_AVAILABLE,
+	MAP_ET_MESSAGE_DELETED,
+	MAP_ET_MESSAGE_SHIFT
+};
+
+struct map_event {
+	enum map_event_type type;
+	char *handle;
+	char *folder;
+	char *old_folder;
+	char *msg_type;
+};
diff --git a/obexd/client/mns.c b/obexd/client/mns.c
new file mode 100644
index 0000000..81550ef
--- /dev/null
+++ b/obexd/client/mns.c
@@ -0,0 +1,348 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2013  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  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 <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include <gobex/gobex.h>
+#include <gobex/gobex-apparam.h>
+
+#include "obexd.h"
+#include "plugin.h"
+#include "log.h"
+#include "obex.h"
+#include "service.h"
+#include "mimetype.h"
+#include "map_ap.h"
+#include "map-event.h"
+
+struct mns_session {
+	GString *buffer;
+	GObexApparam *inparams;
+	uint8_t mas_instance_id;
+};
+
+static const uint8_t MNS_TARGET[TARGET_SIZE] = {
+			0xbb, 0x58, 0x2b, 0x41, 0x42, 0x0c, 0x11, 0xdb,
+			0xb0, 0xde, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66  };
+
+static int get_params(struct obex_session *os, struct mns_session *mns)
+{
+	const uint8_t *buffer;
+	ssize_t size;
+
+	size = obex_get_apparam(os, &buffer);
+	if (size < 0)
+		size = 0;
+
+	mns->inparams = g_obex_apparam_decode(buffer, size);
+	if (mns->inparams == NULL) {
+		DBG("Error when parsing parameters!");
+		return -EBADR;
+	}
+
+	return 0;
+}
+
+static void reset_request(struct mns_session *mns)
+{
+	if (mns->buffer) {
+		g_string_free(mns->buffer, TRUE);
+		mns->buffer = NULL;
+	}
+
+	if (mns->inparams) {
+		g_obex_apparam_free(mns->inparams);
+		mns->inparams = NULL;
+	}
+}
+
+static void mns_session_free(struct mns_session *mns)
+{
+	reset_request(mns);
+	g_free(mns);
+}
+
+static void *mns_connect(struct obex_session *os, int *err)
+{
+	struct mns_session *mns;
+
+	DBG("");
+
+	mns = g_new0(struct mns_session, 1);
+
+	manager_register_session(os);
+
+	return mns;
+}
+
+static void mns_disconnect(struct obex_session *os, void *user_data)
+{
+	struct mns_session *mns = user_data;
+
+	DBG("");
+
+	manager_unregister_session(os);
+
+	mns_session_free(mns);
+}
+
+static int mns_put(struct obex_session *os, void *user_data)
+{
+	struct mns_session *mns = user_data;
+	const char *type = obex_get_type(os);
+	const char *name = obex_get_name(os);
+	int ret;
+
+	DBG("PUT: name %s type %s mns %p", name, type, mns);
+
+	if (type == NULL)
+		return -EBADR;
+
+	ret = get_params(os, mns);
+	if (ret < 0)
+		goto failed;
+
+	ret = obex_put_stream_start(os, name);
+	if (ret < 0)
+		goto failed;
+
+	return 0;
+
+failed:
+	reset_request(mns);
+
+	return ret;
+}
+
+static void parse_event_report_type(struct map_event *event, const char *value)
+{
+	if (!g_ascii_strcasecmp(value, "NewMessage"))
+		event->type = MAP_ET_NEW_MESSAGE;
+	else if (!g_ascii_strcasecmp(value, "DeliverySuccess"))
+		event->type = MAP_ET_DELIVERY_SUCCESS;
+	else if (!g_ascii_strcasecmp(value, "SendingSuccess"))
+		event->type = MAP_ET_SENDING_SUCCESS;
+	else if (!g_ascii_strcasecmp(value, "DeliveryFailure"))
+		event->type = MAP_ET_DELIVERY_FAILURE;
+	else if (!g_ascii_strcasecmp(value, "SendingFailure"))
+		event->type = MAP_ET_SENDING_FAILURE;
+	else if (!g_ascii_strcasecmp(value, "MemoryFull"))
+		event->type = MAP_ET_MEMORY_FULL;
+	else if (!g_ascii_strcasecmp(value, "MemoryAvailable"))
+		event->type = MAP_ET_MEMORY_AVAILABLE;
+	else if (!g_ascii_strcasecmp(value, "MessageDeleted"))
+		event->type = MAP_ET_MESSAGE_DELETED;
+	else if (!g_ascii_strcasecmp(value, "MessageShift"))
+		event->type = MAP_ET_MESSAGE_SHIFT;
+}
+
+static void parse_event_report_handle(struct map_event *event,
+							const char *value)
+{
+	event->handle = g_strdup(value);
+}
+
+static void parse_event_report_folder(struct map_event *event,
+							const char *value)
+{
+	event->folder = g_strdup(value);
+}
+
+static void parse_event_report_old_folder(struct map_event *event,
+							const char *value)
+{
+	event->old_folder = g_strdup(value);
+}
+
+static void parse_event_report_msg_type(struct map_event *event,
+							const char *value)
+{
+	event->msg_type = g_strdup(value);
+}
+
+static struct map_event_report_parser {
+	const char *name;
+	void (*func) (struct map_event *event, const char *value);
+} event_report_parsers[] = {
+		{ "type", parse_event_report_type },
+		{ "handle", parse_event_report_handle },
+		{ "folder", parse_event_report_folder },
+		{ "old_folder", parse_event_report_old_folder },
+		{ "msg_type", parse_event_report_msg_type },
+		{ }
+};
+
+static void event_report_element(GMarkupParseContext *ctxt,
+				const gchar *element, const gchar **names,
+				const gchar **values, gpointer user_data,
+								GError **gerr)
+{
+	struct map_event *event = user_data;
+	const gchar *key;
+	gint i;
+
+	if (strcasecmp("event", element) != 0)
+		return;
+
+	for (i = 0, key = names[i]; key; key = names[++i]) {
+		struct map_event_report_parser *parser;
+
+		for (parser = event_report_parsers; parser && parser->name;
+								parser++) {
+			if (strcasecmp(key, parser->name) == 0) {
+				parser->func(event, values[i]);
+				break;
+			}
+		}
+	}
+}
+
+static const GMarkupParser event_report_parser = {
+	event_report_element,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static void map_event_free(struct map_event *event)
+{
+	g_free(event->handle);
+	g_free(event->folder);
+	g_free(event->old_folder);
+	g_free(event->msg_type);
+	g_free(event);
+}
+
+static void *event_report_open(const char *name, int oflag, mode_t mode,
+				void *driver_data, size_t *size, int *err)
+{
+	struct mns_session *mns = driver_data;
+
+	DBG("");
+
+	g_obex_apparam_get_uint8(mns->inparams, MAP_AP_MASINSTANCEID,
+							&mns->mas_instance_id);
+
+	mns->buffer = g_string_new("");
+
+	if (*err < 0)
+		return NULL;
+	else
+		return mns;
+}
+
+static int event_report_close(void *obj)
+{
+	struct mns_session *mns = obj;
+	GMarkupParseContext *ctxt;
+	struct map_event *event;
+
+	event = g_new0(struct map_event, 1);
+	ctxt = g_markup_parse_context_new(&event_report_parser, 0, event,
+									NULL);
+	g_markup_parse_context_parse(ctxt, mns->buffer->str, mns->buffer->len,
+									NULL);
+	g_markup_parse_context_free(ctxt);
+
+	DBG("Received event report for instance %d", mns->mas_instance_id);
+	DBG("type=%x, handle=%s, folder=%s, old_folder=%s, msg_type=%s",
+				event->type, event->handle, event->folder,
+					event->old_folder, event->msg_type);
+
+	map_event_free(event);
+
+	reset_request(mns);
+
+	return 0;
+}
+
+static ssize_t event_report_write(void *obj, const void *buf, size_t count)
+{
+	struct mns_session *mns = obj;
+
+	DBG("");
+
+	g_string_append_len(mns->buffer, buf, count);
+	return count;
+}
+
+static struct obex_service_driver mns = {
+	.name = "Message Notification server",
+	.service = OBEX_MNS,
+	.target = MNS_TARGET,
+	.target_size = TARGET_SIZE,
+	.connect = mns_connect,
+	.put = mns_put,
+	.disconnect = mns_disconnect,
+};
+
+static struct obex_mime_type_driver mime_event_report = {
+	.target = MNS_TARGET,
+	.target_size = TARGET_SIZE,
+	.mimetype = "x-bt/MAP-event-report",
+	.open = event_report_open,
+	.close = event_report_close,
+	.write = event_report_write,
+};
+
+static struct obex_mime_type_driver *mas_drivers[] = {
+	&mime_event_report,
+	NULL
+};
+
+static int mns_init(void)
+{
+	int err;
+
+	err = obex_mime_type_driver_register(&mime_event_report);
+	if (err < 0)
+		goto fail_mime_event;
+
+	err = obex_service_driver_register(&mns);
+	if (err < 0)
+		goto fail_mns_reg;
+
+	return 0;
+
+fail_mns_reg:
+	obex_mime_type_driver_unregister(&mime_event_report);
+fail_mime_event:
+	return err;
+}
+
+static void mns_exit(void)
+{
+	obex_service_driver_unregister(&mns);
+	obex_mime_type_driver_unregister(&mime_event_report);
+}
+
+OBEX_PLUGIN_DEFINE(mns, mns_init, mns_exit)
diff --git a/obexd/plugins/bluetooth.c b/obexd/plugins/bluetooth.c
index f80faa4..b9e9c91 100644
--- a/obexd/plugins/bluetooth.c
+++ b/obexd/plugins/bluetooth.c
@@ -336,6 +336,8 @@ static const char *service2uuid(uint16_t service)
 		return "00000002-0000-1000-8000-0002ee000002";
 	case OBEX_MAS:
 		return OBEX_MAS_UUID;
+	case OBEX_MNS:
+		return OBEX_MNS_UUID;
 	}
 
 	return NULL;
diff --git a/obexd/src/obexd.h b/obexd/src/obexd.h
index 1c9b2b3..42c3c4d 100644
--- a/obexd/src/obexd.h
+++ b/obexd/src/obexd.h
@@ -29,6 +29,7 @@
 #define OBEX_PCSUITE	(1 << 6)
 #define OBEX_SYNCEVOLUTION	(1 << 7)
 #define OBEX_MAS	(1 << 8)
+#define OBEX_MNS	(1 << 9)
 
 gboolean plugin_init(const char *pattern, const char *exclude);
 void plugin_cleanup(void);
diff --git a/src/profile.c b/src/profile.c
index 631a03f..89eed11 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -435,6 +435,44 @@
 		</attribute>						\
 	</record>"
 
+#define MNS_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"0x1133\"/>		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\"/>	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0003\"/>	\
+					<uint8 value=\"0x%02x\"/>	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0008\"/>	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x1134\"/>	\
+					<uint16 value=\"0x%04x\"/>	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\"/>				\
+		</attribute>						\
+		<attribute id=\"0x0200\">				\
+			<uint16 value=\"%u\" name=\"psm\"/>		\
+		</attribute>						\
+	</record>"
+
 #define SYNC_RECORD							\
 	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
 	<record>							\
@@ -1709,6 +1747,20 @@ static char *get_mas_record(struct ext_profile *ext, struct ext_io *l2cap,
 								ext->name);
 }
 
+static char *get_mns_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	uint16_t psm = 0;
+	uint8_t chan = 0;
+
+	if (l2cap)
+		psm = l2cap->psm;
+	if (rfcomm)
+		chan = rfcomm->chan;
+
+	return g_strdup_printf(MNS_RECORD, chan, ext->version, ext->name, psm);
+}
+
 static char *get_sync_record(struct ext_profile *ext, struct ext_io *l2cap,
 							struct ext_io *rfcomm)
 {
@@ -1899,6 +1951,9 @@ static struct default_settings {
 		.uuid		= OBEX_MNS_UUID,
 		.name		= "Message Notification",
 		.channel	= MNS_DEFAULT_CHANNEL,
+		.psm		= BTD_PROFILE_PSM_AUTO,
+		.get_record	= get_mns_record,
+		.version	= 0x0100
 	},
 };
 
-- 
1.8.1.2


^ permalink raw reply related

* [RFC 3/8] obexd: Read MAP client SDP attributes
From: Christian Fetzer @ 2013-02-06 14:45 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1360161922-6585-1-git-send-email-christian.fetzer@oss.bmw-carit.de>

From: Christian Fetzer <christian.fetzer@bmw-carit.de>

Parse SDP attributes mas_instance_id and supported_message_types from
the transport's service attributes as soon as a connection is
established.
---
 lib/sdp.h          |  2 ++
 obexd/client/map.c | 35 +++++++++++++++++++++++++++++++++--
 2 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/lib/sdp.h b/lib/sdp.h
index a81e857..c3a0261 100644
--- a/lib/sdp.h
+++ b/lib/sdp.h
@@ -294,6 +294,8 @@ extern "C" {
 #define SDP_ATTR_SUPPORTED_FUNCTIONS		0x0312
 #define SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY	0x0313
 #define SDP_ATTR_SUPPORTED_REPOSITORIES		0x0314
+#define SDP_ATTR_MAS_INSTANCE_ID		0x0315
+#define SDP_ATTR_SUPPORTED_MESSAGE_TYPES	0x0316
 
 #define SDP_ATTR_SPECIFICATION_ID		0x0200
 #define SDP_ATTR_VENDOR_ID			0x0201
diff --git a/obexd/client/map.c b/obexd/client/map.c
index ea5681a..463d905 100644
--- a/obexd/client/map.c
+++ b/obexd/client/map.c
@@ -39,6 +39,8 @@
 #include "transfer.h"
 #include "session.h"
 #include "driver.h"
+#include "sdp.h"
+#include "sdp_lib.h"
 
 #define OBEX_MAS_UUID \
 	"\xBB\x58\x2B\x40\x42\x0C\x11\xDB\xB0\xDE\x08\x00\x20\x0C\x9A\x66"
@@ -93,6 +95,8 @@ struct map_data {
 	struct obc_session *session;
 	DBusMessage *msg;
 	GHashTable *messages;
+	int16_t mas_instance_id;
+	uint8_t supported_message_types;
 };
 
 #define MAP_MSG_FLAG_PRIORITY	0x01
@@ -1538,6 +1542,30 @@ static void map_free(void *data)
 	g_free(map);
 }
 
+static void parse_service_record(struct map_data *map)
+{
+	sdp_data_t *data;
+	const sdp_record_t *rec = obc_session_get_service_record(map->session);
+
+	if (!rec)
+		return;
+
+	/* MAS instance id */
+	map->mas_instance_id = -1;
+	data = sdp_data_get(rec, SDP_ATTR_MAS_INSTANCE_ID);
+	if (data != NULL)
+		map->mas_instance_id = data->val.uint8;
+	else
+		DBG("Failed to read MAS instance id");
+
+	/* Supported Message Types */
+	data = sdp_data_get(rec, SDP_ATTR_SUPPORTED_MESSAGE_TYPES);
+	if (data != NULL)
+		map->supported_message_types = data->val.uint8;
+	else
+		DBG("Failed to read supported message types");
+}
+
 static int map_probe(struct obc_session *session)
 {
 	struct map_data *map;
@@ -1545,8 +1573,6 @@ static int map_probe(struct obc_session *session)
 
 	path = obc_session_get_path(session);
 
-	DBG("%s", path);
-
 	map = g_try_new0(struct map_data, 1);
 	if (!map)
 		return -ENOMEM;
@@ -1555,6 +1581,10 @@ static int map_probe(struct obc_session *session)
 	map->messages = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
 								map_msg_remove);
 
+	parse_service_record(map);
+
+	DBG("%s, instance id %d", path, map->mas_instance_id);
+
 	if (!g_dbus_register_interface(conn, path, MAP_INTERFACE, map_methods,
 					NULL, NULL, map, map_free)) {
 		map_free(map);
@@ -1579,6 +1609,7 @@ static struct obc_driver map = {
 	.uuid = MAS_UUID,
 	.target = OBEX_MAS_UUID,
 	.target_len = OBEX_MAS_UUID_LEN,
+	.force_service_lookup = TRUE,
 	.probe = map_probe,
 	.remove = map_remove
 };
-- 
1.8.1.2


^ permalink raw reply related

* [RFC 2/8] obexd: Provide SDP record in get_service_record
From: Christian Fetzer @ 2013-02-06 14:45 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1360161922-6585-1-git-send-email-christian.fetzer@oss.bmw-carit.de>

From: Christian Fetzer <christian.fetzer@bmw-carit.de>

This caches the SDP record of the active session in the bluetooth transport
and makes the data available for clients in get_service_record.
The cached SDP record can be retreived using obc_session_get_service_record.

To have the SDP record also available when directly connecting by port,
the profile implementation has to specify force_service_lookup in its
obc_driver struct.

Profiles like MAP specify additional SDP attributes that are of interest
for the profile implementation.
---
 obexd/client/bluetooth.c | 51 +++++++++++++++++++++++++++++++++++++++++-------
 obexd/client/driver.h    |  1 +
 obexd/client/session.c   |  5 +++--
 obexd/client/transport.h |  1 +
 4 files changed, 49 insertions(+), 9 deletions(-)

diff --git a/obexd/client/bluetooth.c b/obexd/client/bluetooth.c
index a832a3f..07c2228 100644
--- a/obexd/client/bluetooth.c
+++ b/obexd/client/bluetooth.c
@@ -51,7 +51,9 @@ struct bluetooth_session {
 	bdaddr_t src;
 	bdaddr_t dst;
 	uint16_t port;
+	gboolean force_service_lookup;
 	sdp_session_t *sdp;
+	sdp_record_t *sdp_record;
 	GIOChannel *io;
 	char *service;
 	obc_transport_func func;
@@ -82,6 +84,9 @@ static void session_destroy(struct bluetooth_session *session)
 	if (session->sdp)
 		sdp_close(session->sdp);
 
+	if (session->sdp_record)
+		sdp_record_free(session->sdp_record);
+
 	g_free(session->service);
 	g_free(session);
 }
@@ -160,7 +165,7 @@ static void search_callback(uint8_t type, uint16_t status,
 		sdp_record_t *rec;
 		sdp_list_t *protos;
 		sdp_data_t *data;
-		int recsize, ch = -1;
+		int recsize, ch = -1, psm = -1;
 
 		recsize = 0;
 		rec = sdp_extract_pdu(rsp, bytesleft, &recsize);
@@ -183,15 +188,31 @@ static void search_callback(uint8_t type, uint16_t status,
 		data = sdp_data_get(rec, 0x0200);
 		/* PSM must be odd and lsb of upper byte must be 0 */
 		if (data != NULL && (data->val.uint16 & 0x0101) == 0x0001)
-			ch = data->val.uint16;
-
-		sdp_record_free(rec);
+			psm = data->val.uint16;
+
+		/* Preferably connect using L2CAP directly instead of RFCOMM.
+		 * If the session has already a port specified, use that to
+		 * connect. */
+		if (session->port == 0) {
+			if (psm > 0)
+				port = psm;
+			else if (ch > 0)
+				port = ch;
+		} else if (session->port > 0 &&
+				(session->port == ch || session->port == psm)) {
+			port = session->port;
+		}
 
-		if (ch > 0) {
-			port = ch;
+		/* Cache the sdp record associated with the service that we
+		 * attempt to connect. This allows reading it's application
+		 * specific parameters with get_service_record. */
+		if (port > 0) {
+			session->sdp_record = rec;
 			break;
 		}
 
+		sdp_record_free(rec);
+
 		scanned += recsize;
 		rsp += recsize;
 		bytesleft -= recsize;
@@ -362,7 +383,7 @@ static int session_connect(struct bluetooth_session *session)
 
 	DBG("session %p", session);
 
-	if (session->port > 0) {
+	if (session->port > 0 && !session->force_service_lookup) {
 		session->io = transport_connect(&session->src, &session->dst,
 							session->port,
 							transport_callback,
@@ -379,6 +400,7 @@ static int session_connect(struct bluetooth_session *session)
 
 static guint bluetooth_connect(const char *source, const char *destination,
 				const char *service, uint16_t port,
+				gboolean force_service_lookup,
 				obc_transport_func func, void *user_data)
 {
 	struct bluetooth_session *session;
@@ -398,6 +420,7 @@ static guint bluetooth_connect(const char *source, const char *destination,
 	session->func = func;
 	session->port = port;
 	session->user_data = user_data;
+	session->force_service_lookup = force_service_lookup;
 
 	session->service = g_strdup(service);
 	str2ba(destination, &session->dst);
@@ -459,11 +482,25 @@ static int bluetooth_getpacketopt(GIOChannel *io, int *tx_mtu, int *rx_mtu)
 	return 0;
 }
 
+static const void *get_bluetooth_service_record(guint id)
+{
+	GSList *l;
+
+	for (l = sessions; l; l = l->next) {
+		struct bluetooth_session *session = l->data;
+
+		if (session->id == id)
+			return session->sdp_record;
+	}
+	return NULL;
+}
+
 static struct obc_transport bluetooth = {
 	.name = "Bluetooth",
 	.connect = bluetooth_connect,
 	.getpacketopt = bluetooth_getpacketopt,
 	.disconnect = bluetooth_disconnect,
+	.get_service_record = get_bluetooth_service_record,
 };
 
 int bluetooth_init(void)
diff --git a/obexd/client/driver.h b/obexd/client/driver.h
index f1c0646..08ea09b 100644
--- a/obexd/client/driver.h
+++ b/obexd/client/driver.h
@@ -26,6 +26,7 @@ struct obc_driver {
 	const char *uuid;
 	void *target;
 	gsize target_len;
+	gboolean force_service_lookup;
 	int (*probe) (struct obc_session *session);
 	void (*remove) (struct obc_session *session);
 };
diff --git a/obexd/client/session.c b/obexd/client/session.c
index 32f58c3..dbbbc8e 100644
--- a/obexd/client/session.c
+++ b/obexd/client/session.c
@@ -407,8 +407,9 @@ static int session_connect(struct obc_session *session,
 	}
 
 	session->id = transport->connect(session->source, session->destination,
-					driver->uuid, session->channel,
-					transport_func, callback);
+						driver->uuid, session->channel,
+						driver->force_service_lookup,
+						transport_func, callback);
 	if (session->id == 0) {
 		obc_session_unref(callback->session);
 		g_free(callback);
diff --git a/obexd/client/transport.h b/obexd/client/transport.h
index eb29bf3..b131c50 100644
--- a/obexd/client/transport.h
+++ b/obexd/client/transport.h
@@ -28,6 +28,7 @@ struct obc_transport {
 	const char *name;
 	guint (*connect) (const char *source, const char *destination,
 				const char *service, uint16_t port,
+				gboolean force_service_lookup,
 				obc_transport_func func, void *user_data);
 	int (*getpacketopt) (GIOChannel *io, int *tx_mtu, int *rx_mtu);
 	void (*disconnect) (guint id);
-- 
1.8.1.2


^ permalink raw reply related

* [RFC 1/8] obexd: Add get_service_record to obc_transport
From: Christian Fetzer @ 2013-02-06 14:45 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1360161922-6585-1-git-send-email-christian.fetzer@oss.bmw-carit.de>

From: Christian Fetzer <christian.fetzer@bmw-carit.de>

This adds a get_service_record function pointer to obc_transport,
which allows transports to provide the session specific service record.
This record can be queried using obc_session_get_service_record.
---
 obexd/client/session.c   | 6 ++++++
 obexd/client/session.h   | 2 ++
 obexd/client/transport.h | 1 +
 3 files changed, 9 insertions(+)

diff --git a/obexd/client/session.c b/obexd/client/session.c
index db37a86..32f58c3 100644
--- a/obexd/client/session.c
+++ b/obexd/client/session.c
@@ -827,6 +827,12 @@ fail:
 	return NULL;
 }
 
+const void *obc_session_get_service_record(struct obc_session *session)
+{
+	if (session->id > 0 && session->transport != NULL)
+		return session->transport->get_service_record(session->id);
+}
+
 const char *obc_session_get_owner(struct obc_session *session)
 {
 	if (session == NULL)
diff --git a/obexd/client/session.h b/obexd/client/session.h
index 402c042..8a3d629 100644
--- a/obexd/client/session.h
+++ b/obexd/client/session.h
@@ -54,6 +54,8 @@ const char *obc_session_get_target(struct obc_session *session);
 const char *obc_session_register(struct obc_session *session,
 						GDBusDestroyFunction destroy);
 
+const void *obc_session_get_service_record(struct obc_session *session);
+
 guint obc_session_queue(struct obc_session *session,
 				struct obc_transfer *transfer,
 				session_callback_t func, void *user_data,
diff --git a/obexd/client/transport.h b/obexd/client/transport.h
index 5140840..eb29bf3 100644
--- a/obexd/client/transport.h
+++ b/obexd/client/transport.h
@@ -31,6 +31,7 @@ struct obc_transport {
 				obc_transport_func func, void *user_data);
 	int (*getpacketopt) (GIOChannel *io, int *tx_mtu, int *rx_mtu);
 	void (*disconnect) (guint id);
+	const void *(*get_service_record)(guint id);
 };
 
 int obc_transport_register(struct obc_transport *transport);
-- 
1.8.1.2


^ permalink raw reply related

* [RFC 0/8] MAP client: notification support
From: Christian Fetzer @ 2013-02-06 14:45 UTC (permalink / raw)
  To: linux-bluetooth

From: Christian Fetzer <christian.fetzer@bmw-carit.de>

This patchset adds the necessary blocks for receiving notifications in the MAP client.
So far this covers only bluetooth protocol related additions, leaving out end user D-Bus API changes.

The patchset consists of the following parts:
- Message Notification Server (MNS)
  Once connected, the MNS will receive XML based event reports from the MSE and parse them into a struct
  for further processing.

- RegisterNotification function
  The function sends the SetNotificationRegistration command to the MSE which will then try to connect
  to the MNS.

- Notification dispatcher (that dispatches event reports by their MAS instance id to the correct client instance)
  The message access part of every client instance registers a notification handler function.
  The MNS will call the associated handler for all received event report.

Since the dispatching needs to be done on the mas instance id, the message access part of the client
needs to be aware of the instance id of the server it connects to. Unfortunately this information is only
available as SDP attribute.

Due to the current transport layer abstraction in obexd, the MAP client has no direct access to SDP.
Therefore I have extended obc_transport to allow concrete transports (like Bluetooth) to specify
a get_service_record function and made it available through obc_session_get_service_record.
This way, transports can provide additional service information to the client implementations.
The bluetooth transport will now always do a sdp lookup (if the driver's obc_driver->force_service_lookup is set).
In addition it will cache sdp record associated with the connection and make it accessible in get_service_record.

Christian Fetzer (8):
  obexd: Add get_service_record to obc_transport
  obexd: Provide SDP record in get_service_record
  obexd: Read MAP client SDP attributes
  obexd: Add Message Notification Server (MNS)
  obexd: Add RegisterNotifications function
  obexd: Add MAP notification dispatcher
  obexd: Register MAP notification handler
  obexd: Notify registered notification handlers

 Makefile.obexd                |   4 +
 doc/obex-api.txt              |   7 +
 lib/sdp.h                     |   2 +
 obexd/client/bluetooth.c      |  51 ++++++-
 obexd/client/driver.h         |   1 +
 obexd/client/map-dispatcher.c |  86 +++++++++++
 obexd/client/map-dispatcher.h |  45 ++++++
 obexd/client/map-event.h      |  42 +++++
 obexd/client/map.c            | 144 +++++++++++++++++-
 obexd/client/mns.c            | 346 ++++++++++++++++++++++++++++++++++++++++++
 obexd/client/session.c        |  11 +-
 obexd/client/session.h        |   2 +
 obexd/client/transport.h      |   2 +
 obexd/plugins/bluetooth.c     |   2 +
 obexd/src/obexd.h             |   1 +
 src/profile.c                 |  55 +++++++
 16 files changed, 790 insertions(+), 11 deletions(-)
 create mode 100644 obexd/client/map-dispatcher.c
 create mode 100644 obexd/client/map-dispatcher.h
 create mode 100644 obexd/client/map-event.h
 create mode 100644 obexd/client/mns.c

-- 
1.8.1.2


^ permalink raw reply

* [RFC v1 7/7] profile: Limit to one remote UUID per profile
From: Mikel Astiz @ 2013-02-06  9:16 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1360142187-15347-1-git-send-email-mikel.astiz.oss@gmail.com>

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

The code can be considerably simplified by constraining struct
btd_profile to one single remote UUID.
---
 profiles/audio/manager.c             |  8 +++---
 profiles/cyclingspeed/cyclingspeed.c |  2 +-
 profiles/deviceinfo/deviceinfo.c     |  2 +-
 profiles/gatt/gas.c                  |  2 +-
 profiles/health/hdp_manager.c        |  4 +--
 profiles/heartrate/heartrate.c       |  2 +-
 profiles/input/hog.c                 |  2 +-
 profiles/input/manager.c             |  2 +-
 profiles/network/manager.c           |  6 ++---
 profiles/proximity/manager.c         |  8 +++---
 profiles/scanparam/scan.c            |  2 +-
 profiles/thermometer/thermometer.c   |  2 +-
 src/device.c                         | 49 +++++++++++++-----------------------
 src/profile.c                        | 25 ++++++++----------
 src/profile.h                        |  4 +--
 15 files changed, 49 insertions(+), 71 deletions(-)

diff --git a/profiles/audio/manager.c b/profiles/audio/manager.c
index 3023249..5799e77 100644
--- a/profiles/audio/manager.c
+++ b/profiles/audio/manager.c
@@ -345,7 +345,7 @@ static struct btd_profile a2dp_source_profile = {
 	.name		= "audio-source",
 	.priority	= BTD_PROFILE_PRIORITY_MEDIUM,
 
-	.remote_uuids	= BTD_UUIDS(A2DP_SOURCE_UUID),
+	.remote_uuid	= A2DP_SOURCE_UUID,
 	.device_probe	= a2dp_source_probe,
 	.device_remove	= audio_remove,
 
@@ -361,7 +361,7 @@ static struct btd_profile a2dp_sink_profile = {
 	.name		= "audio-sink",
 	.priority	= BTD_PROFILE_PRIORITY_MEDIUM,
 
-	.remote_uuids	= BTD_UUIDS(A2DP_SINK_UUID),
+	.remote_uuid	= A2DP_SINK_UUID,
 	.device_probe	= a2dp_sink_probe,
 	.device_remove	= audio_remove,
 
@@ -376,7 +376,7 @@ static struct btd_profile a2dp_sink_profile = {
 static struct btd_profile avrcp_target_profile = {
 	.name		= "audio-avrcp-target",
 
-	.remote_uuids	= BTD_UUIDS(AVRCP_TARGET_UUID),
+	.remote_uuid	= AVRCP_TARGET_UUID,
 	.device_probe	= avrcp_probe,
 	.device_remove	= audio_remove,
 
@@ -391,7 +391,7 @@ static struct btd_profile avrcp_target_profile = {
 static struct btd_profile avrcp_remote_profile = {
 	.name		= "audio-avrcp-control",
 
-	.remote_uuids	= BTD_UUIDS(AVRCP_REMOTE_UUID),
+	.remote_uuid	= AVRCP_REMOTE_UUID,
 	.device_probe	= avrcp_probe,
 	.device_remove	= audio_remove,
 
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index fc72791..125007e 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -1256,7 +1256,7 @@ static void csc_device_remove(struct btd_profile *p,
 
 static struct btd_profile cscp_profile = {
 	.name		= "Cycling Speed and Cadence GATT Driver",
-	.remote_uuids	= BTD_UUIDS(CYCLING_SC_UUID),
+	.remote_uuid	= CYCLING_SC_UUID,
 
 	.adapter_probe	= csc_adapter_probe,
 	.adapter_remove	= csc_adapter_remove,
diff --git a/profiles/deviceinfo/deviceinfo.c b/profiles/deviceinfo/deviceinfo.c
index fb423fa..471241b 100644
--- a/profiles/deviceinfo/deviceinfo.c
+++ b/profiles/deviceinfo/deviceinfo.c
@@ -219,7 +219,7 @@ static void deviceinfo_driver_remove(struct btd_profile *p,
 
 static struct btd_profile deviceinfo_profile = {
 	.name		= "deviceinfo",
-	.remote_uuids	= BTD_UUIDS(DEVICE_INFORMATION_UUID),
+	.remote_uuid	= DEVICE_INFORMATION_UUID,
 	.device_probe	= deviceinfo_driver_probe,
 	.device_remove	= deviceinfo_driver_remove
 };
diff --git a/profiles/gatt/gas.c b/profiles/gatt/gas.c
index 6a8571c..bc8dbb5 100644
--- a/profiles/gatt/gas.c
+++ b/profiles/gatt/gas.c
@@ -431,7 +431,7 @@ static void gatt_driver_remove(struct btd_profile *p,
 
 static struct btd_profile gatt_profile = {
 	.name		= "gap-gatt-profile",
-	.remote_uuids	= BTD_UUIDS(GATT_UUID),
+	.remote_uuid	= GATT_UUID,
 	.device_probe	= gatt_driver_probe,
 	.device_remove	= gatt_driver_remove
 };
diff --git a/profiles/health/hdp_manager.c b/profiles/health/hdp_manager.c
index 9df5b2b..5428724 100644
--- a/profiles/health/hdp_manager.c
+++ b/profiles/health/hdp_manager.c
@@ -67,7 +67,7 @@ static void hdp_driver_remove(struct btd_profile *p, struct btd_device *device)
 
 static struct btd_profile hdp_source_profile = {
 	.name		= "hdp-source",
-	.remote_uuids	= BTD_UUIDS(HDP_SOURCE_UUID),
+	.remote_uuid	= HDP_SOURCE_UUID,
 
 	.device_probe	= hdp_driver_probe,
 	.device_remove	= hdp_driver_remove,
@@ -78,7 +78,7 @@ static struct btd_profile hdp_source_profile = {
 
 static struct btd_profile hdp_sink_profile = {
 	.name		= "hdp-sink",
-	.remote_uuids	= BTD_UUIDS(HDP_SINK_UUID),
+	.remote_uuid	= HDP_SINK_UUID,
 
 	.device_probe	= hdp_driver_probe,
 	.device_remove	= hdp_driver_remove,
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 5c56d3f..0520f5c 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -861,7 +861,7 @@ static void heartrate_device_remove(struct btd_profile *p,
 
 static struct btd_profile hrp_profile = {
 	.name		= "Heart Rate GATT Driver",
-	.remote_uuids	= BTD_UUIDS(HEART_RATE_UUID),
+	.remote_uuid	= HEART_RATE_UUID,
 
 	.device_probe	= heartrate_device_probe,
 	.device_remove	= heartrate_device_remove,
diff --git a/profiles/input/hog.c b/profiles/input/hog.c
index a5269d9..eadc860 100644
--- a/profiles/input/hog.c
+++ b/profiles/input/hog.c
@@ -872,7 +872,7 @@ static void hog_remove(struct btd_profile *p, struct btd_device *device)
 
 static struct btd_profile hog_profile = {
 	.name		= "input-hog",
-	.remote_uuids	= BTD_UUIDS(HOG_UUID),
+	.remote_uuid	= HOG_UUID,
 	.device_probe	= hog_probe,
 	.device_remove	= hog_remove,
 };
diff --git a/profiles/input/manager.c b/profiles/input/manager.c
index 6ed12ee..d30ba67 100644
--- a/profiles/input/manager.c
+++ b/profiles/input/manager.c
@@ -89,7 +89,7 @@ static void hid_server_remove(struct btd_profile *p,
 static struct btd_profile input_profile = {
 	.name		= "input-hid",
 	.local_uuid	= HID_UUID,
-	.remote_uuids	= BTD_UUIDS(HID_UUID),
+	.remote_uuid	= HID_UUID,
 
 	.auto_connect	= true,
 	.connect	= input_device_connect,
diff --git a/profiles/network/manager.c b/profiles/network/manager.c
index bc553c4..53bb652 100644
--- a/profiles/network/manager.c
+++ b/profiles/network/manager.c
@@ -195,7 +195,7 @@ static void nap_server_remove(struct btd_profile *p,
 static struct btd_profile panu_profile = {
 	.name		= "network-panu",
 	.local_uuid	= NAP_UUID,
-	.remote_uuids	= BTD_UUIDS(PANU_UUID),
+	.remote_uuid	= PANU_UUID,
 	.device_probe	= panu_probe,
 	.device_remove	= network_remove,
 	.connect	= panu_connect,
@@ -207,7 +207,7 @@ static struct btd_profile panu_profile = {
 static struct btd_profile gn_profile = {
 	.name		= "network-gn",
 	.local_uuid	= PANU_UUID,
-	.remote_uuids	= BTD_UUIDS(GN_UUID),
+	.remote_uuid	= GN_UUID,
 	.device_probe	= gn_probe,
 	.device_remove	= network_remove,
 	.connect	= gn_connect,
@@ -219,7 +219,7 @@ static struct btd_profile gn_profile = {
 static struct btd_profile nap_profile = {
 	.name		= "network-nap",
 	.local_uuid	= PANU_UUID,
-	.remote_uuids	= BTD_UUIDS(NAP_UUID),
+	.remote_uuid	= NAP_UUID,
 	.device_probe	= nap_probe,
 	.device_remove	= network_remove,
 	.connect	= nap_connect,
diff --git a/profiles/proximity/manager.c b/profiles/proximity/manager.c
index 7579be5..81bfc3b 100644
--- a/profiles/proximity/manager.c
+++ b/profiles/proximity/manager.c
@@ -104,28 +104,28 @@ static void monitor_txpower_remove(struct btd_profile *p,
 
 static struct btd_profile pxp_monitor_linkloss_profile = {
 	.name		= "proximity-linkloss",
-	.remote_uuids	= BTD_UUIDS(LINK_LOSS_UUID),
+	.remote_uuid	= LINK_LOSS_UUID,
 	.device_probe	= monitor_linkloss_probe,
 	.device_remove	= monitor_linkloss_remove,
 };
 
 static struct btd_profile pxp_monitor_immediate_profile = {
 	.name		= "proximity-immediate",
-	.remote_uuids	= BTD_UUIDS(IMMEDIATE_ALERT_UUID),
+	.remote_uuid	= IMMEDIATE_ALERT_UUID,
 	.device_probe	= monitor_immediate_probe,
 	.device_remove	= monitor_immediate_remove,
 };
 
 static struct btd_profile pxp_monitor_txpower_profile = {
 	.name		= "proximity-txpower",
-	.remote_uuids	= BTD_UUIDS(TX_POWER_UUID),
+	.remote_uuid	= TX_POWER_UUID,
 	.device_probe	= monitor_txpower_probe,
 	.device_remove	= monitor_txpower_remove,
 };
 
 static struct btd_profile pxp_reporter_profile = {
 	.name		= "Proximity Reporter GATT Driver",
-	.remote_uuids	= BTD_UUIDS(GATT_UUID),
+	.remote_uuid	= GATT_UUID,
 	.device_probe	= reporter_device_probe,
 	.device_remove	= reporter_device_remove,
 
diff --git a/profiles/scanparam/scan.c b/profiles/scanparam/scan.c
index 268bdc8..abbd129 100644
--- a/profiles/scanparam/scan.c
+++ b/profiles/scanparam/scan.c
@@ -287,7 +287,7 @@ static void scan_param_remove(struct btd_profile *p, struct btd_device *device)
 
 static struct btd_profile scan_profile = {
 	.name = "Scan Parameters Client Driver",
-	.remote_uuids = BTD_UUIDS(SCAN_PARAMETERS_UUID),
+	.remote_uuid = SCAN_PARAMETERS_UUID,
 	.device_probe = scan_param_probe,
 	.device_remove = scan_param_remove,
 };
diff --git a/profiles/thermometer/thermometer.c b/profiles/thermometer/thermometer.c
index 1b299e7..8550500 100644
--- a/profiles/thermometer/thermometer.c
+++ b/profiles/thermometer/thermometer.c
@@ -1313,7 +1313,7 @@ static void thermometer_adapter_remove(struct btd_profile *p,
 
 static struct btd_profile thermometer_profile = {
 	.name		= "Health Thermometer GATT driver",
-	.remote_uuids	= BTD_UUIDS(HEALTH_THERMOMETER_UUID),
+	.remote_uuid	= HEALTH_THERMOMETER_UUID,
 	.device_probe	= thermometer_device_probe,
 	.device_remove	= thermometer_device_remove,
 	.adapter_probe	= thermometer_adapter_probe,
diff --git a/src/device.c b/src/device.c
index 49f8957..adcad6c 100644
--- a/src/device.c
+++ b/src/device.c
@@ -1155,15 +1155,12 @@ static struct btd_profile *find_connectable_profile(struct btd_device *dev,
 
 	for (l = dev->profiles; l != NULL; l = g_slist_next(l)) {
 		struct btd_profile *p = l->data;
-		int i;
 
-		if (!p->connect || !p->remote_uuids)
+		if (!p->connect || !p->remote_uuid)
 			continue;
 
-		for (i = 0; p->remote_uuids[i] != NULL; i++) {
-			if (strcasecmp(uuid, p->remote_uuids[i]) == 0)
-				return p;
-		}
+		if (strcasecmp(uuid, p->remote_uuid) == 0)
+			return p;
 	}
 
 	return NULL;
@@ -2299,27 +2296,18 @@ GSList *device_get_uuids(struct btd_device *device)
 	return device->uuids;
 }
 
-static GSList *device_match_profile(struct btd_device *device,
+static bool device_match_profile(struct btd_device *device,
 					struct btd_profile *profile,
 					GSList *uuids)
 {
-	const char **uuid;
-	GSList *match_uuids = NULL;
-
-	for (uuid = profile->remote_uuids; *uuid; uuid++) {
-		GSList *match;
-
-		/* skip duplicated uuids */
-		if (g_slist_find_custom(match_uuids, *uuid, bt_uuid_strcmp))
-			continue;
+	if (profile->remote_uuid == NULL)
+		return false;
 
-		/* match profile uuid */
-		match = g_slist_find_custom(uuids, *uuid, bt_uuid_strcmp);
-		if (match)
-			match_uuids = g_slist_append(match_uuids, match->data);
-	}
+	if (g_slist_find_custom(uuids, profile->remote_uuid,
+							bt_uuid_strcmp) == NULL)
+		return false;
 
-	return match_uuids;
+	return true;
 }
 
 struct probe_data {
@@ -2337,10 +2325,11 @@ static void dev_probe(struct btd_profile *p, void *user_data)
 	if (p->device_probe == NULL)
 		return;
 
-	probe_uuids = device_match_profile(d->dev, p, d->uuids);
-	if (!probe_uuids)
+	if (!device_match_profile(d->dev, p, d->uuids))
 		return;
 
+	probe_uuids = g_slist_append(NULL, (char *) p->remote_uuid);
+
 	err = p->device_probe(p, d->dev, probe_uuids);
 	if (err < 0) {
 		error("%s profile probe failed for %s", p->name, d->addr);
@@ -2363,10 +2352,11 @@ void device_probe_profile(gpointer a, gpointer b)
 	if (profile->device_probe == NULL)
 		return;
 
-	probe_uuids = device_match_profile(device, profile, device->uuids);
-	if (!probe_uuids)
+	if (!device_match_profile(device, profile, device->uuids))
 		return;
 
+	probe_uuids = g_slist_append(NULL, (char *) profile->remote_uuid);
+
 	ba2str(&device->bdaddr, addr);
 
 	err = profile->device_probe(profile, device, probe_uuids);
@@ -2451,15 +2441,10 @@ static void device_remove_profiles(struct btd_device *device, GSList *uuids)
 
 	for (l = device->profiles; l != NULL; l = next) {
 		struct btd_profile *profile = l->data;
-		GSList *probe_uuids;
 
 		next = l->next;
-		probe_uuids = device_match_profile(device, profile,
-								device->uuids);
-		if (probe_uuids != NULL) {
-			g_slist_free(probe_uuids);
+		if (device_match_profile(device, profile, device->uuids))
 			continue;
-		}
 
 		profile->device_remove(profile, device);
 		device->profiles = g_slist_remove(device->profiles, profile);
diff --git a/src/profile.c b/src/profile.c
index 631a03f..b31bcc5 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -519,7 +519,7 @@ struct ext_profile {
 	char *(*get_record)(struct ext_profile *ext, struct ext_io *l2cap,
 							struct ext_io *rfcomm);
 
-	char **remote_uuids;
+	char *remote_uuid;
 
 	guint id;
 
@@ -851,7 +851,7 @@ static bool send_new_connection(struct ext_profile *ext, struct ext_io *conn)
 	DBusMessage *msg;
 	DBusMessageIter iter, dict;
 	struct prop_append_data data = { &dict, conn };
-	const char *remote_uuid = ext->remote_uuids[0];
+	const char *remote_uuid = ext->remote_uuid;
 	const sdp_record_t *rec;
 	const char *path;
 	int fd;
@@ -1540,7 +1540,7 @@ static int resolve_service(struct ext_io *conn, const bdaddr_t *src,
 	uuid_t uuid;
 	int err;
 
-	bt_string2uuid(&uuid, ext->remote_uuids[0]);
+	bt_string2uuid(&uuid, ext->remote_uuid);
 	sdp_uuid128_to_uuid(&uuid);
 
 	err = bt_search_service(src, dst, &uuid, record_cb, conn, NULL);
@@ -1910,8 +1910,7 @@ static void ext_set_defaults(struct ext_profile *ext)
 	ext->authorize = true;
 	ext->enable_client = true;
 	ext->enable_server = true;
-
-	ext->remote_uuids = g_new0(char *, 2);
+	ext->remote_uuid = NULL;
 
 	for (i = 0; i < G_N_ELEMENTS(defaults); i++) {
 		struct default_settings *settings = &defaults[i];
@@ -1925,7 +1924,7 @@ static void ext_set_defaults(struct ext_profile *ext)
 		else
 			remote_uuid = ext->uuid;
 
-		ext->remote_uuids[0] = g_strdup(remote_uuid);
+		ext->remote_uuid = g_strdup(remote_uuid);
 
 		if (settings->channel)
 			ext->local_chan = settings->channel;
@@ -2117,21 +2116,18 @@ static struct ext_profile *create_ext(const char *owner, const char *path,
 	if (!ext->name)
 		ext->name = g_strdup_printf("%s%s/%s", owner, path, uuid);
 
-	if (!ext->remote_uuids[0]) {
+	if (!ext->remote_uuid) {
 		if (ext->service)
-			ext->remote_uuids[0] = g_strdup(ext->service);
+			ext->remote_uuid = g_strdup(ext->service);
 		else
-			ext->remote_uuids[0] = g_strdup(ext->uuid);
+			ext->remote_uuid = g_strdup(ext->uuid);
 	}
 
 	p = &ext->p;
 
 	p->name = ext->name;
 	p->local_uuid = ext->service ? ext->service : ext->uuid;
-
-	/* Typecast can't really be avoided here:
-	 * http://c-faq.com/ansi/constmismatch.html */
-	p->remote_uuids = (const char **) ext->remote_uuids;
+	p->remote_uuid = ext->remote_uuid;
 
 	if (ext->enable_server || ext->record || ext->get_record) {
 		p->adapter_probe = ext_adapter_probe;
@@ -2167,8 +2163,7 @@ static void remove_ext(struct ext_profile *ext)
 	g_slist_free_full(ext->servers, ext_io_destroy);
 	g_slist_free_full(ext->conns, ext_io_destroy);
 
-	g_strfreev(ext->remote_uuids);
-
+	g_free(ext->remote_uuid);
 	g_free(ext->name);
 	g_free(ext->owner);
 	g_free(ext->uuid);
diff --git a/src/profile.h b/src/profile.h
index d858925..5d78b37 100644
--- a/src/profile.h
+++ b/src/profile.h
@@ -21,8 +21,6 @@
  *
  */
 
-#define BTD_UUIDS(args...) ((const char *[]) { args, NULL } )
-
 #define BTD_PROFILE_PRIORITY_LOW	0
 #define BTD_PROFILE_PRIORITY_MEDIUM	1
 #define BTD_PROFILE_PRIORITY_HIGH	2
@@ -32,7 +30,7 @@ struct btd_profile {
 	int priority;
 
 	const char *local_uuid;
-	const char **remote_uuids;
+	const char *remote_uuid;
 
 	bool auto_connect;
 
-- 
1.8.1


^ permalink raw reply related

* [RFC v1 6/7] health: Split health into two btd_profile
From: Mikel Astiz @ 2013-02-06  9:16 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1360142187-15347-1-git-send-email-mikel.astiz.oss@gmail.com>

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

Register a separate btd_profile for each health device role.
---
 profiles/health/hdp_manager.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/profiles/health/hdp_manager.c b/profiles/health/hdp_manager.c
index 87e70cd..9df5b2b 100644
--- a/profiles/health/hdp_manager.c
+++ b/profiles/health/hdp_manager.c
@@ -65,9 +65,9 @@ static void hdp_driver_remove(struct btd_profile *p, struct btd_device *device)
 	hdp_device_unregister(device);
 }
 
-static struct btd_profile hdp_profile = {
-	.name		= "hdp-profile",
-	.remote_uuids	= BTD_UUIDS(HDP_UUID, HDP_SOURCE_UUID, HDP_SINK_UUID),
+static struct btd_profile hdp_source_profile = {
+	.name		= "hdp-source",
+	.remote_uuids	= BTD_UUIDS(HDP_SOURCE_UUID),
 
 	.device_probe	= hdp_driver_probe,
 	.device_remove	= hdp_driver_remove,
@@ -76,19 +76,29 @@ static struct btd_profile hdp_profile = {
 	.adapter_remove	= hdp_adapter_remove,
 };
 
+static struct btd_profile hdp_sink_profile = {
+	.name		= "hdp-sink",
+	.remote_uuids	= BTD_UUIDS(HDP_SINK_UUID),
+
+	.device_probe	= hdp_driver_probe,
+	.device_remove	= hdp_driver_remove,
+};
+
 int hdp_manager_init(void)
 {
 	if (hdp_manager_start() < 0)
 		return -1;
 
-	btd_profile_register(&hdp_profile);
+	btd_profile_register(&hdp_source_profile);
+	btd_profile_register(&hdp_sink_profile);
 
 	return 0;
 }
 
 void hdp_manager_exit(void)
 {
-	btd_profile_unregister(&hdp_profile);
+	btd_profile_unregister(&hdp_sink_profile);
+	btd_profile_unregister(&hdp_source_profile);
 
 	hdp_manager_stop();
 }
-- 
1.8.1


^ permalink raw reply related

* [RFC v1 5/7] gatt: List only GATT_UUID as remote UUID
From: Mikel Astiz @ 2013-02-06  9:16 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1360142187-15347-1-git-send-email-mikel.astiz.oss@gmail.com>

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

The probe function checks if both UUIDs are present, so there is no
need to list both in btd_profile.
---
 profiles/gatt/gas.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/profiles/gatt/gas.c b/profiles/gatt/gas.c
index 9360201..6a8571c 100644
--- a/profiles/gatt/gas.c
+++ b/profiles/gatt/gas.c
@@ -431,7 +431,7 @@ static void gatt_driver_remove(struct btd_profile *p,
 
 static struct btd_profile gatt_profile = {
 	.name		= "gap-gatt-profile",
-	.remote_uuids	= BTD_UUIDS(GAP_UUID, GATT_UUID),
+	.remote_uuids	= BTD_UUIDS(GATT_UUID),
 	.device_probe	= gatt_driver_probe,
 	.device_remove	= gatt_driver_remove
 };
-- 
1.8.1


^ permalink raw reply related

* [RFC v1 4/7] proximity: Split monitor into three btd_profile
From: Mikel Astiz @ 2013-02-06  9:16 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1360142187-15347-1-git-send-email-mikel.astiz.oss@gmail.com>

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

Split into three btd_profile such that each of them handles one single
UUID.
---
 profiles/proximity/manager.c | 102 +++++++++++++++++++++++++++++++------------
 1 file changed, 75 insertions(+), 27 deletions(-)

diff --git a/profiles/proximity/manager.c b/profiles/proximity/manager.c
index 1c07267..7579be5 100644
--- a/profiles/proximity/manager.c
+++ b/profiles/proximity/manager.c
@@ -48,42 +48,79 @@ static struct enabled enabled  = {
 	.findme = TRUE,
 };
 
-static int monitor_device_probe(struct btd_profile *p,
+static int monitor_linkloss_probe(struct btd_profile *p,
 				struct btd_device *device, GSList *uuids)
 {
-	struct gatt_primary *linkloss, *txpower, *immediate;
-	int err = 0;
+	struct gatt_primary *linkloss;
 
-	immediate = btd_device_get_primary(device, IMMEDIATE_ALERT_UUID);
-	txpower = btd_device_get_primary(device, TX_POWER_UUID);
 	linkloss = btd_device_get_primary(device, LINK_LOSS_UUID);
+	if (linkloss == NULL)
+		return -1;
 
-	if (linkloss)
-		err = monitor_register_linkloss(device, &enabled, linkloss);
+	return monitor_register_linkloss(device, &enabled, linkloss);
+}
 
-	if (err >= 0 && txpower)
-		err = monitor_register_txpower(device, &enabled, txpower);
+static int monitor_immediate_probe(struct btd_profile *p,
+				struct btd_device *device, GSList *uuids)
+{
+	struct gatt_primary *immediate;
 
-	if (err >= 0 && immediate)
-		err = monitor_register_immediate(device, &enabled, immediate);
+	immediate = btd_device_get_primary(device, IMMEDIATE_ALERT_UUID);
+	if (immediate == NULL)
+		return -1;
 
-	return err;
+	return monitor_register_immediate(device, &enabled, immediate);
 }
 
-static void monitor_device_remove(struct btd_profile *p,
+static int monitor_txpower_probe(struct btd_profile *p,
+				struct btd_device *device, GSList *uuids)
+{
+	struct gatt_primary *txpower;
+
+	txpower = btd_device_get_primary(device, TX_POWER_UUID);
+	if (txpower == NULL)
+		return -1;
+
+	return monitor_register_txpower(device, &enabled, txpower);
+}
+
+static void monitor_linkloss_remove(struct btd_profile *p,
+						struct btd_device *device)
+{
+	monitor_unregister_linkloss(device);
+}
+
+static void monitor_immediate_remove(struct btd_profile *p,
 						struct btd_device *device)
 {
 	monitor_unregister_immediate(device);
+}
+
+static void monitor_txpower_remove(struct btd_profile *p,
+						struct btd_device *device)
+{
 	monitor_unregister_txpower(device);
-	monitor_unregister_linkloss(device);
 }
 
-static struct btd_profile pxp_monitor_profile = {
-	.name		= "Proximity Monitor GATT Driver",
-	.remote_uuids	= BTD_UUIDS(IMMEDIATE_ALERT_UUID,
-						LINK_LOSS_UUID, TX_POWER_UUID),
-	.device_probe	= monitor_device_probe,
-	.device_remove	= monitor_device_remove,
+static struct btd_profile pxp_monitor_linkloss_profile = {
+	.name		= "proximity-linkloss",
+	.remote_uuids	= BTD_UUIDS(LINK_LOSS_UUID),
+	.device_probe	= monitor_linkloss_probe,
+	.device_remove	= monitor_linkloss_remove,
+};
+
+static struct btd_profile pxp_monitor_immediate_profile = {
+	.name		= "proximity-immediate",
+	.remote_uuids	= BTD_UUIDS(IMMEDIATE_ALERT_UUID),
+	.device_probe	= monitor_immediate_probe,
+	.device_remove	= monitor_immediate_remove,
+};
+
+static struct btd_profile pxp_monitor_txpower_profile = {
+	.name		= "proximity-txpower",
+	.remote_uuids	= BTD_UUIDS(TX_POWER_UUID),
+	.device_probe	= monitor_txpower_probe,
+	.device_remove	= monitor_txpower_remove,
 };
 
 static struct btd_profile pxp_reporter_profile = {
@@ -122,19 +159,30 @@ int proximity_manager_init(GKeyFile *config)
 {
 	load_config_file(config);
 
-	if (btd_profile_register(&pxp_monitor_profile) < 0)
-		return -1;
+	if (btd_profile_register(&pxp_monitor_linkloss_profile) < 0)
+		goto fail;
 
-	if (btd_profile_register(&pxp_reporter_profile) < 0) {
-		btd_profile_unregister(&pxp_monitor_profile);
-		return -1;
-	}
+	if (btd_profile_register(&pxp_monitor_immediate_profile) < 0)
+		goto fail;
+
+	if (btd_profile_register(&pxp_monitor_txpower_profile) < 0)
+		goto fail;
+
+	if (btd_profile_register(&pxp_reporter_profile) < 0)
+		goto fail;
 
 	return 0;
+
+fail:
+	proximity_manager_exit();
+
+	return -1;
 }
 
 void proximity_manager_exit(void)
 {
-	btd_profile_unregister(&pxp_monitor_profile);
 	btd_profile_unregister(&pxp_reporter_profile);
+	btd_profile_unregister(&pxp_monitor_txpower_profile);
+	btd_profile_unregister(&pxp_monitor_immediate_profile);
+	btd_profile_unregister(&pxp_monitor_linkloss_profile);
 }
-- 
1.8.1


^ permalink raw reply related

* [RFC v1 3/7] proximity: Split internal monitor registration API
From: Mikel Astiz @ 2013-02-06  9:16 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1360142187-15347-1-git-send-email-mikel.astiz.oss@gmail.com>

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

Split the monitor registration API into three independent registrations
each of them taking one specific GATT primary.
---
 profiles/proximity/manager.c |  16 +++-
 profiles/proximity/monitor.c | 220 ++++++++++++++++++++++++++++++++++---------
 profiles/proximity/monitor.h |  17 +++-
 3 files changed, 204 insertions(+), 49 deletions(-)

diff --git a/profiles/proximity/manager.c b/profiles/proximity/manager.c
index b405f15..1c07267 100644
--- a/profiles/proximity/manager.c
+++ b/profiles/proximity/manager.c
@@ -52,18 +52,30 @@ static int monitor_device_probe(struct btd_profile *p,
 				struct btd_device *device, GSList *uuids)
 {
 	struct gatt_primary *linkloss, *txpower, *immediate;
+	int err = 0;
 
 	immediate = btd_device_get_primary(device, IMMEDIATE_ALERT_UUID);
 	txpower = btd_device_get_primary(device, TX_POWER_UUID);
 	linkloss = btd_device_get_primary(device, LINK_LOSS_UUID);
 
-	return monitor_register(device, linkloss, txpower, immediate, &enabled);
+	if (linkloss)
+		err = monitor_register_linkloss(device, &enabled, linkloss);
+
+	if (err >= 0 && txpower)
+		err = monitor_register_txpower(device, &enabled, txpower);
+
+	if (err >= 0 && immediate)
+		err = monitor_register_immediate(device, &enabled, immediate);
+
+	return err;
 }
 
 static void monitor_device_remove(struct btd_profile *p,
 						struct btd_device *device)
 {
-	monitor_unregister(device);
+	monitor_unregister_immediate(device);
+	monitor_unregister_txpower(device);
+	monitor_unregister_linkloss(device);
 }
 
 static struct btd_profile pxp_monitor_profile = {
diff --git a/profiles/proximity/monitor.c b/profiles/proximity/monitor.c
index 597f161..489ccdd 100644
--- a/profiles/proximity/monitor.c
+++ b/profiles/proximity/monitor.c
@@ -34,6 +34,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/stat.h>
+#include <glib.h>
 
 #include <bluetooth/bluetooth.h>
 
@@ -83,6 +84,22 @@ struct monitor {
 	guint attioid;
 };
 
+static GSList *monitors = NULL;
+
+static struct monitor *find_monitor(struct btd_device *device)
+{
+	GSList *l;
+
+	for (l = monitors; l; l = l->next) {
+		struct monitor *monitor = l->data;
+
+		if (monitor->device == device)
+			return monitor;
+	}
+
+	return NULL;
+}
+
 static void write_proximity_config(struct btd_device *device, const char *alert,
 					const char *level)
 {
@@ -580,33 +597,25 @@ static void monitor_destroy(gpointer user_data)
 {
 	struct monitor *monitor = user_data;
 
-	if (monitor->immediateto)
-		g_source_remove(monitor->immediateto);
-
-	if (monitor->attioid)
-		btd_device_remove_attio_callback(monitor->device,
-						monitor->attioid);
-	if (monitor->attrib)
-		g_attrib_unref(monitor->attrib);
-
 	btd_device_unref(monitor->device);
-	g_free(monitor->linkloss);
-	g_free(monitor->immediate);
-	g_free(monitor->txpower);
 	g_free(monitor->linklosslevel);
 	g_free(monitor->immediatelevel);
 	g_free(monitor->signallevel);
 	g_free(monitor);
+
+	monitors = g_slist_remove(monitors, monitor);
 }
 
-int monitor_register(struct btd_device *device,
-		struct gatt_primary *linkloss, struct gatt_primary *txpower,
-		struct gatt_primary *immediate, struct enabled *enabled)
+static struct monitor *register_monitor(struct btd_device *device)
 {
 	const char *path = device_get_path(device);
 	struct monitor *monitor;
 	char *level;
 
+	monitor = find_monitor(device);
+	if (monitor != NULL)
+		return monitor;
+
 	level = read_proximity_config(device, "LinkLossAlertLevel");
 
 	monitor = g_new0(struct monitor, 1);
@@ -615,6 +624,8 @@ int monitor_register(struct btd_device *device,
 	monitor->signallevel = g_strdup("unknown");
 	monitor->immediatelevel = g_strdup("none");
 
+	monitors = g_slist_append(monitors, monitor);
+
 	if (g_dbus_register_interface(btd_get_dbus_connection(), path,
 				PROXIMITY_INTERFACE,
 				NULL, NULL, monitor_device_properties,
@@ -622,55 +633,178 @@ int monitor_register(struct btd_device *device,
 		error("D-Bus failed to register %s interface",
 						PROXIMITY_INTERFACE);
 		monitor_destroy(monitor);
-		return -1;
+		return NULL;
 	}
 
 	DBG("Registered interface %s on path %s", PROXIMITY_INTERFACE, path);
 
-	if (linkloss && enabled->linkloss) {
-		monitor->linkloss = g_new0(struct att_range, 1);
-		monitor->linkloss->start = linkloss->range.start;
-		monitor->linkloss->end = linkloss->range.end;
-
-		monitor->enabled.linkloss = TRUE;
-	}
-
-	if (immediate) {
-		if (txpower && enabled->pathloss) {
-			monitor->txpower = g_new0(struct att_range, 1);
-			monitor->txpower->start = txpower->range.start;
-			monitor->txpower->end = txpower->range.end;
-
-			monitor->enabled.pathloss = TRUE;
-		}
-
-		if (enabled->pathloss || enabled->findme) {
-			monitor->immediate = g_new0(struct att_range, 1);
-			monitor->immediate->start = immediate->range.start;
-			monitor->immediate->end = immediate->range.end;
-		}
+	return monitor;
+}
 
-		monitor->enabled.findme = enabled->findme;
-	}
+static void update_monitor(struct monitor *monitor)
+{
+	if (monitor->txpower != NULL && monitor->immediate != NULL)
+		monitor->enabled.pathloss = TRUE;
+	else
+		monitor->enabled.pathloss = FALSE;
 
 	DBG("Link Loss: %s, Path Loss: %s, FindMe: %s",
 				monitor->enabled.linkloss ? "TRUE" : "FALSE",
 				monitor->enabled.pathloss ? "TRUE" : "FALSE",
 				monitor->enabled.findme ? "TRUE" : "FALSE");
 
-	if (monitor->enabled.linkloss || monitor->enabled.pathloss)
-		monitor->attioid = btd_device_add_attio_callback(device,
+	if (!monitor->enabled.linkloss && !monitor->enabled.pathloss)
+		return;
+
+	if (monitor->attioid != 0)
+		return;
+
+	monitor->attioid = btd_device_add_attio_callback(monitor->device,
 							attio_connected_cb,
 							attio_disconnected_cb,
 							monitor);
+}
+
+int monitor_register_linkloss(struct btd_device *device,
+						struct enabled *enabled,
+						struct gatt_primary *linkloss)
+{
+	struct monitor *monitor;
+
+	if (!enabled->linkloss)
+		return 0;
+
+	monitor = register_monitor(device);
+	if (monitor == NULL)
+		return -1;
+
+	monitor->linkloss = g_new0(struct att_range, 1);
+	monitor->linkloss->start = linkloss->range.start;
+	monitor->linkloss->end = linkloss->range.end;
+	monitor->enabled.linkloss = TRUE;
+
+	update_monitor(monitor);
 
 	return 0;
 }
 
-void monitor_unregister(struct btd_device *device)
+int monitor_register_txpower(struct btd_device *device,
+						struct enabled *enabled,
+						struct gatt_primary *txpower)
 {
+	struct monitor *monitor;
+
+	if (!enabled->pathloss)
+		return 0;
+
+	monitor = register_monitor(device);
+	if (monitor == NULL)
+		return -1;
+
+	monitor->txpower = g_new0(struct att_range, 1);
+	monitor->txpower->start = txpower->range.start;
+	monitor->txpower->end = txpower->range.end;
+
+	update_monitor(monitor);
+
+	return 0;
+}
+
+int monitor_register_immediate(struct btd_device *device,
+						struct enabled *enabled,
+						struct gatt_primary *immediate)
+{
+	struct monitor *monitor;
+
+	if (!enabled->pathloss && !enabled->findme)
+		return 0;
+
+	monitor = register_monitor(device);
+	if (monitor == NULL)
+		return -1;
+
+	monitor->immediate = g_new0(struct att_range, 1);
+	monitor->immediate->start = immediate->range.start;
+	monitor->immediate->end = immediate->range.end;
+	monitor->enabled.findme = enabled->findme;
+
+	update_monitor(monitor);
+
+	return 0;
+}
+
+static void cleanup_monitor(struct monitor *monitor)
+{
+	struct btd_device *device = monitor->device;
 	const char *path = device_get_path(device);
 
+	if (monitor->immediate != NULL || monitor->txpower != NULL)
+		return;
+
+	if (monitor->immediateto != 0) {
+		g_source_remove(monitor->immediateto);
+		monitor->immediateto = 0;
+	}
+
+	if (monitor->attioid != 0) {
+		btd_device_remove_attio_callback(device, monitor->attioid);
+		monitor->attioid = 0;
+	}
+
+	if (monitor->attrib != NULL) {
+		g_attrib_unref(monitor->attrib);
+		monitor->attrib = NULL;
+	}
+
+	if (monitor->linkloss != NULL)
+		return;
+
 	g_dbus_unregister_interface(btd_get_dbus_connection(), path,
 							PROXIMITY_INTERFACE);
 }
+
+void monitor_unregister_linkloss(struct btd_device *device)
+{
+	struct monitor *monitor;
+
+	monitor = find_monitor(device);
+	if (monitor == NULL)
+		return;
+
+	g_free(monitor->linkloss);
+	monitor->linkloss = NULL;
+	monitor->enabled.linkloss = TRUE;
+
+	cleanup_monitor(monitor);
+}
+
+void monitor_unregister_txpower(struct btd_device *device)
+{
+	struct monitor *monitor;
+
+	monitor = find_monitor(device);
+	if (monitor == NULL)
+		return;
+
+	g_free(monitor->txpower);
+	monitor->txpower = NULL;
+	monitor->enabled.pathloss = FALSE;
+
+	cleanup_monitor(monitor);
+}
+
+void monitor_unregister_immediate(struct btd_device *device)
+{
+	struct monitor *monitor;
+
+	monitor = find_monitor(device);
+	if (monitor == NULL)
+		return;
+
+	g_free(monitor->immediate);
+	monitor->immediate = NULL;
+	monitor->enabled.findme = FALSE;
+	monitor->enabled.pathloss = FALSE;
+
+	cleanup_monitor(monitor);
+}
diff --git a/profiles/proximity/monitor.h b/profiles/proximity/monitor.h
index 191b562..d9a40c6 100644
--- a/profiles/proximity/monitor.h
+++ b/profiles/proximity/monitor.h
@@ -28,7 +28,16 @@ struct enabled {
 	gboolean findme;
 };
 
-int monitor_register(struct btd_device *device, struct gatt_primary *linkloss,
-		struct gatt_primary *txpower, struct gatt_primary *immediate,
-		struct enabled *enabled);
-void monitor_unregister(struct btd_device *device);
+int monitor_register_linkloss(struct btd_device *device,
+						struct enabled *enabled,
+						struct gatt_primary *linkloss);
+int monitor_register_txpower(struct btd_device *device,
+						struct enabled *enabled,
+						struct gatt_primary *txpower);
+int monitor_register_immediate(struct btd_device *device,
+						struct enabled *enabled,
+						struct gatt_primary *immediate);
+
+void monitor_unregister_linkloss(struct btd_device *device);
+void monitor_unregister_txpower(struct btd_device *device);
+void monitor_unregister_immediate(struct btd_device *device);
-- 
1.8.1


^ permalink raw reply related

* [RFC v1 2/7] audio: Split AVRCP into two btd_profile
From: Mikel Astiz @ 2013-02-06  9:16 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1360142187-15347-1-git-send-email-mikel.astiz.oss@gmail.com>

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

Register a separate btd_profile for each role of AVRCP.
---
 profiles/audio/avrcp.c   | 80 +++++++++++++++++++++++++++++++++++++-----------
 profiles/audio/avrcp.h   |  6 ++--
 profiles/audio/manager.c | 71 ++++++++++++++++++++++++++++++------------
 3 files changed, 118 insertions(+), 39 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 277f9f3..fe6333b 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2712,41 +2712,63 @@ static struct avrcp_server *avrcp_server_register(struct btd_adapter *adapter,
 	return server;
 }
 
-int avrcp_register(struct btd_adapter *adapter, GKeyFile *config)
+int avrcp_target_register(struct btd_adapter *adapter, GKeyFile *config)
 {
 	sdp_record_t *record;
 	struct avrcp_server *server;
 
+	server = find_server(servers, adapter);
+	if (server != NULL)
+		goto done;
+
 	server = avrcp_server_register(adapter, config);
 	if (server == NULL)
 		return -EPROTONOSUPPORT;
 
+done:
 	record = avrcp_tg_record();
 	if (!record) {
 		error("Unable to allocate new service record");
-		avrcp_unregister(adapter);
+		avrcp_target_unregister(adapter);
 		return -1;
 	}
 
 	if (add_record_to_server(adapter_get_address(adapter), record) < 0) {
 		error("Unable to register AVRCP target service record");
-		avrcp_unregister(adapter);
+		avrcp_target_unregister(adapter);
 		sdp_record_free(record);
 		return -1;
 	}
 	server->tg_record_id = record->handle;
 
+	return 0;
+}
+
+int avrcp_remote_register(struct btd_adapter *adapter, GKeyFile *config)
+{
+	sdp_record_t *record;
+	struct avrcp_server *server;
+
+	server = find_server(servers, adapter);
+	if (server != NULL)
+		goto done;
+
+	server = avrcp_server_register(adapter, config);
+	if (server == NULL)
+		return -EPROTONOSUPPORT;
+
+done:
 	record = avrcp_ct_record();
 	if (!record) {
 		error("Unable to allocate new service record");
-		avrcp_unregister(adapter);
+		avrcp_remote_unregister(adapter);
 		return -1;
 	}
 
 	if (add_record_to_server(adapter_get_address(adapter), record) < 0) {
 		error("Unable to register AVRCP service record");
 		sdp_record_free(record);
-		avrcp_unregister(adapter);
+		avrcp_remote_unregister(adapter);
 		return -1;
 	}
 	server->ct_record_id = record->handle;
@@ -2754,25 +2776,13 @@ int avrcp_register(struct btd_adapter *adapter, GKeyFile *config)
 	return 0;
 }
 
-void avrcp_unregister(struct btd_adapter *adapter)
+static void avrcp_server_unregister(struct avrcp_server *server)
 {
-	struct avrcp_server *server;
-
-	server = find_server(servers, adapter);
-	if (!server)
-		return;
-
 	g_slist_free_full(server->sessions, g_free);
 	g_slist_free_full(server->players, player_destroy);
 
 	servers = g_slist_remove(servers, server);
 
-	if (server->ct_record_id != 0)
-		remove_record_from_server(server->ct_record_id);
-
-	if (server->tg_record_id != 0)
-		remove_record_from_server(server->tg_record_id);
-
 	avctp_unregister(server->adapter);
 	btd_adapter_unref(server->adapter);
 	g_free(server);
@@ -2786,6 +2796,40 @@ void avrcp_unregister(struct btd_adapter *adapter)
 	}
 }
 
+void avrcp_target_unregister(struct btd_adapter *adapter)
+{
+	struct avrcp_server *server;
+
+	server = find_server(servers, adapter);
+	if (!server)
+		return;
+
+	if (server->tg_record_id != 0) {
+		remove_record_from_server(server->tg_record_id);
+		server->tg_record_id = 0;
+	}
+
+	if (server->ct_record_id == 0)
+		avrcp_server_unregister(server);
+}
+
+void avrcp_remote_unregister(struct btd_adapter *adapter)
+{
+	struct avrcp_server *server;
+
+	server = find_server(servers, adapter);
+	if (!server)
+		return;
+
+	if (server->ct_record_id != 0) {
+		remove_record_from_server(server->ct_record_id);
+		server->ct_record_id = 0;
+	}
+
+	if (server->tg_record_id == 0)
+		avrcp_server_unregister(server);
+}
+
 struct avrcp_player *avrcp_register_player(struct btd_adapter *adapter,
 						struct avrcp_player_cb *cb,
 						void *user_data,
diff --git a/profiles/audio/avrcp.h b/profiles/audio/avrcp.h
index 3799da1..1f98090 100644
--- a/profiles/audio/avrcp.h
+++ b/profiles/audio/avrcp.h
@@ -93,8 +93,10 @@ struct avrcp_player_cb {
 							void *user_data);
 };
 
-int avrcp_register(struct btd_adapter *adapter, GKeyFile *config);
-void avrcp_unregister(struct btd_adapter *adapter);
+int avrcp_target_register(struct btd_adapter *adapter, GKeyFile *config);
+void avrcp_target_unregister(struct btd_adapter *adapter);
+int avrcp_remote_register(struct btd_adapter *adapter, GKeyFile *config);
+void avrcp_remote_unregister(struct btd_adapter *adapter);
 
 gboolean avrcp_connect(struct audio_device *dev);
 void avrcp_disconnect(struct audio_device *dev);
diff --git a/profiles/audio/manager.c b/profiles/audio/manager.c
index 934227e..3023249 100644
--- a/profiles/audio/manager.c
+++ b/profiles/audio/manager.c
@@ -229,7 +229,7 @@ static int a2dp_sink_disconnect(struct btd_device *dev,
 	return sink_disconnect(audio_dev, FALSE);
 }
 
-static int avrcp_control_connect(struct btd_device *dev,
+static int avrcp_target_connect(struct btd_device *dev,
 						struct btd_profile *profile)
 {
 	const char *path = device_get_path(dev);
@@ -246,7 +246,7 @@ static int avrcp_control_connect(struct btd_device *dev,
 	return control_connect(audio_dev);
 }
 
-static int avrcp_control_disconnect(struct btd_device *dev,
+static int avrcp_target_disconnect(struct btd_device *dev,
 						struct btd_profile *profile)
 {
 	const char *path = device_get_path(dev);
@@ -295,20 +295,36 @@ static void a2dp_sink_server_remove(struct btd_profile *p,
 	a2dp_sink_unregister(adapter);
 }
 
-static int avrcp_server_probe(struct btd_profile *p,
+static int avrcp_target_server_probe(struct btd_profile *p,
 						struct btd_adapter *adapter)
 {
 	DBG("path %s", adapter_get_path(adapter));
 
-	return avrcp_register(adapter, config);
+	return avrcp_target_register(adapter, config);
 }
 
-static void avrcp_server_remove(struct btd_profile *p,
+static int avrcp_remote_server_probe(struct btd_profile *p,
 						struct btd_adapter *adapter)
 {
 	DBG("path %s", adapter_get_path(adapter));
 
-	return avrcp_unregister(adapter);
+	return avrcp_remote_register(adapter, config);
+}
+
+static void avrcp_target_server_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	DBG("path %s", adapter_get_path(adapter));
+
+	avrcp_target_unregister(adapter);
+}
+
+static void avrcp_remote_server_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	DBG("path %s", adapter_get_path(adapter));
+
+	avrcp_remote_unregister(adapter);
 }
 
 static int media_server_probe(struct btd_adapter *adapter)
@@ -357,19 +373,30 @@ static struct btd_profile a2dp_sink_profile = {
 	.adapter_remove	= a2dp_sink_server_remove,
 };
 
-static struct btd_profile avrcp_profile = {
-	.name		= "audio-avrcp",
+static struct btd_profile avrcp_target_profile = {
+	.name		= "audio-avrcp-target",
 
-	.remote_uuids	= BTD_UUIDS(AVRCP_TARGET_UUID, AVRCP_REMOTE_UUID),
+	.remote_uuids	= BTD_UUIDS(AVRCP_TARGET_UUID),
 	.device_probe	= avrcp_probe,
 	.device_remove	= audio_remove,
 
 	.auto_connect	= true,
-	.connect	= avrcp_control_connect,
-	.disconnect	= avrcp_control_disconnect,
+	.connect	= avrcp_target_connect,
+	.disconnect	= avrcp_target_disconnect,
+
+	.adapter_probe	= avrcp_target_server_probe,
+	.adapter_remove = avrcp_target_server_remove,
+};
+
+static struct btd_profile avrcp_remote_profile = {
+	.name		= "audio-avrcp-control",
 
-	.adapter_probe	= avrcp_server_probe,
-	.adapter_remove = avrcp_server_remove,
+	.remote_uuids	= BTD_UUIDS(AVRCP_REMOTE_UUID),
+	.device_probe	= avrcp_probe,
+	.device_remove	= audio_remove,
+
+	.adapter_probe	= avrcp_remote_server_probe,
+	.adapter_remove = avrcp_remote_server_remove,
 };
 
 static struct btd_adapter_driver media_driver = {
@@ -400,12 +427,14 @@ void audio_source_disconnected(struct btd_device *dev, int err)
 
 void audio_control_connected(struct btd_device *dev, int err)
 {
-	device_profile_connected(dev, &avrcp_profile, err);
+	device_profile_connected(dev, &avrcp_target_profile, err);
+	device_profile_connected(dev, &avrcp_remote_profile, err);
 }
 
 void audio_control_disconnected(struct btd_device *dev, int err)
 {
-	device_profile_disconnected(dev, &avrcp_profile, err);
+	device_profile_disconnected(dev, &avrcp_target_profile, err);
+	device_profile_disconnected(dev, &avrcp_remote_profile, err);
 }
 
 int audio_manager_init(GKeyFile *conf)
@@ -449,8 +478,10 @@ proceed:
 	if (enabled.sink)
 		btd_profile_register(&a2dp_sink_profile);
 
-	if (enabled.control)
-		btd_profile_register(&avrcp_profile);
+	if (enabled.control) {
+		btd_profile_register(&avrcp_remote_profile);
+		btd_profile_register(&avrcp_target_profile);
+	}
 
 	btd_register_adapter_driver(&media_driver);
 
@@ -470,8 +501,10 @@ void audio_manager_exit(void)
 	if (enabled.sink)
 		btd_profile_unregister(&a2dp_sink_profile);
 
-	if (enabled.control)
-		btd_profile_unregister(&avrcp_profile);
+	if (enabled.control) {
+		btd_profile_unregister(&avrcp_remote_profile);
+		btd_profile_unregister(&avrcp_target_profile);
+	}
 
 	btd_unregister_adapter_driver(&media_driver);
 }
-- 
1.8.1


^ permalink raw reply related

* [RFC v1 1/7] avrcp: Refactor server registration
From: Mikel Astiz @ 2013-02-06  9:16 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Mikel Astiz
In-Reply-To: <1360142187-15347-1-git-send-email-mikel.astiz.oss@gmail.com>

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

Use a helper function to install the AVRCP server, just like other audio
profiles such as in a2dp.c do.
---
 profiles/audio/avrcp.c | 54 ++++++++++++++++++++++++++++++--------------------
 1 file changed, 32 insertions(+), 22 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 00eeea1..277f9f3 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2681,9 +2681,9 @@ void avrcp_disconnect(struct audio_device *dev)
 	avctp_disconnect(session);
 }
 
-int avrcp_register(struct btd_adapter *adapter, GKeyFile *config)
+static struct avrcp_server *avrcp_server_register(struct btd_adapter *adapter,
+							GKeyFile *config)
 {
-	sdp_record_t *record;
 	gboolean tmp, master = TRUE;
 	GError *err = NULL;
 	struct avrcp_server *server;
@@ -2698,18 +2698,39 @@ int avrcp_register(struct btd_adapter *adapter, GKeyFile *config)
 			master = tmp;
 	}
 
+	if (avctp_register(adapter, master) < 0)
+		return NULL;
+
 	server = g_new0(struct avrcp_server, 1);
+	server->adapter = btd_adapter_ref(adapter);
+
+	servers = g_slist_append(servers, server);
+
+	if (!avctp_id)
+		avctp_id = avctp_add_state_cb(state_changed, NULL);
+
+	return server;
+}
+
+int avrcp_register(struct btd_adapter *adapter, GKeyFile *config)
+{
+	sdp_record_t *record;
+	struct avrcp_server *server;
+
+	server = avrcp_server_register(adapter, config);
+	if (server == NULL)
+		return -EPROTONOSUPPORT;
 
 	record = avrcp_tg_record();
 	if (!record) {
 		error("Unable to allocate new service record");
-		g_free(server);
+		avrcp_unregister(adapter);
 		return -1;
 	}
 
 	if (add_record_to_server(adapter_get_address(adapter), record) < 0) {
 		error("Unable to register AVRCP target service record");
-		g_free(server);
+		avrcp_unregister(adapter);
 		sdp_record_free(record);
 		return -1;
 	}
@@ -2718,32 +2739,18 @@ int avrcp_register(struct btd_adapter *adapter, GKeyFile *config)
 	record = avrcp_ct_record();
 	if (!record) {
 		error("Unable to allocate new service record");
-		g_free(server);
+		avrcp_unregister(adapter);
 		return -1;
 	}
 
 	if (add_record_to_server(adapter_get_address(adapter), record) < 0) {
 		error("Unable to register AVRCP service record");
 		sdp_record_free(record);
-		g_free(server);
+		avrcp_unregister(adapter);
 		return -1;
 	}
 	server->ct_record_id = record->handle;
 
-	if (avctp_register(adapter, master) < 0) {
-		remove_record_from_server(server->ct_record_id);
-		remove_record_from_server(server->tg_record_id);
-		g_free(server);
-		return -1;
-	}
-
-	server->adapter = btd_adapter_ref(adapter);
-
-	servers = g_slist_append(servers, server);
-
-	if (!avctp_id)
-		avctp_id = avctp_add_state_cb(state_changed, NULL);
-
 	return 0;
 }
 
@@ -2760,8 +2767,11 @@ void avrcp_unregister(struct btd_adapter *adapter)
 
 	servers = g_slist_remove(servers, server);
 
-	remove_record_from_server(server->ct_record_id);
-	remove_record_from_server(server->tg_record_id);
+	if (server->ct_record_id != 0)
+		remove_record_from_server(server->ct_record_id);
+
+	if (server->tg_record_id != 0)
+		remove_record_from_server(server->tg_record_id);
 
 	avctp_unregister(server->adapter);
 	btd_adapter_unref(server->adapter);
-- 
1.8.1


^ permalink raw reply related

* [RFC v1 0/7] One remote UUID per btd_profile
From: Mikel Astiz @ 2013-02-06  9:16 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Mikel Astiz

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

There was no feedback for v0, and therefore I resubmit this patchset rebased and with the following main changes:

- AVRCP gets split but both roles share a common config in audio.conf
- Health profile also split into two profiles (patch v1 6/7)
- Core simplified by limiting to one UUID per btd_profile (patch v1 7/7)

There's a lot more that code be simplified after 7/7 but this has been left aside for the moment.

>From original cover-letter:

Disclaimer: this RFC is WIP and hasn't been tested, specially because I don't have the necessary hardware for it.

The goal is to have one remote_uuid per btd_profile. There are only a few exceptions to this rule, and this patchset tries to fix three of them. This basically means splitting one btd_profile per role.

Mikel Astiz (7):
  avrcp: Refactor server registration
  audio: Split AVRCP into two btd_profile
  proximity: Split internal monitor registration API
  proximity: Split monitor into three btd_profile
  gatt: List only GATT_UUID as remote UUID
  health: Split health into two btd_profile
  profile: Limit to one remote UUID per profile

 profiles/audio/avrcp.c               | 114 +++++++++++++-----
 profiles/audio/avrcp.h               |   6 +-
 profiles/audio/manager.c             |  75 ++++++++----
 profiles/cyclingspeed/cyclingspeed.c |   2 +-
 profiles/deviceinfo/deviceinfo.c     |   2 +-
 profiles/gatt/gas.c                  |   2 +-
 profiles/health/hdp_manager.c        |  20 +++-
 profiles/heartrate/heartrate.c       |   2 +-
 profiles/input/hog.c                 |   2 +-
 profiles/input/manager.c             |   2 +-
 profiles/network/manager.c           |   6 +-
 profiles/proximity/manager.c         | 100 ++++++++++++----
 profiles/proximity/monitor.c         | 220 ++++++++++++++++++++++++++++-------
 profiles/proximity/monitor.h         |  17 ++-
 profiles/scanparam/scan.c            |   2 +-
 profiles/thermometer/thermometer.c   |   2 +-
 src/device.c                         |  49 +++-----
 src/profile.c                        |  25 ++--
 src/profile.h                        |   4 +-
 19 files changed, 466 insertions(+), 186 deletions(-)

-- 
1.8.1


^ permalink raw reply

* [PATCH BlueZ 5/5 v2] AVRCP: Create folders for /Filesystem and /NowPlaying
From: Luiz Augusto von Dentz @ 2013-02-06  9:15 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1360142100-5141-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

If browsing is supported create objects representing /Filesystem and
/NowPlaying, these object implement MediaItem interface and can be
passed to ChangeFolder to change the scope.
---
 profiles/audio/avrcp.c  |   7 +-
 profiles/audio/player.c | 232 ++++++++++++++++++++++++++++++++++++++++++------
 profiles/audio/player.h |   2 +
 3 files changed, 215 insertions(+), 26 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 55887bc..3b9aad6 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1984,11 +1984,16 @@ static void avrcp_player_parse_features(struct avrcp_player *player,
 
 	player->features = g_memdup(features, 16);
 
-	if (features[7] & 0x08)
+	if (features[7] & 0x08) {
 		media_player_set_browsable(mp, true);
+		media_player_create_folder(mp, "/Filesystem");
+	}
 
 	if (features[7] & 0x10)
 		media_player_set_searchable(mp, true);
+
+	if (features[8] & 0x02)
+		media_player_create_folder(mp, "/NowPlaying");
 }
 
 static void avrcp_parse_media_player_item(struct avrcp *session,
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 558aee5..9ff21cf 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -45,6 +45,7 @@
 
 #define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
 #define MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
+#define MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
 
 struct player_callback {
 	const struct media_player_callback *cbs;
@@ -57,9 +58,17 @@ struct pending_req {
 	const char *value;
 };
 
+struct media_item {
+	struct media_player	*player;
+	char			*path;		/* Item object path */
+	char			*name;		/* Item name */
+	bool			folder;		/* Item folder flag */
+	bool			playable;	/* Item playable flag */
+};
+
 struct media_folder {
-	char			*name;		/* Folder name */
-	uint32_t		items;		/* Number of items */
+	struct media_item	*item;		/* Folder item */
+	uint32_t		number_of_items;/* Number of items */
 };
 
 struct media_player {
@@ -79,6 +88,7 @@ struct media_player {
 	guint			process_id;
 	struct player_callback	*cb;
 	GSList			*pending;
+	GSList			*folders;
 };
 
 static void append_metadata(void *key, void *value, void *user_data)
@@ -588,25 +598,27 @@ static gboolean folder_name_exists(const GDBusPropertyTable *property,
 								void *data)
 {
 	struct media_player *mp = data;
+	struct media_folder *folder = mp->folder;
 
-	if (mp->folder == NULL)
+	if (folder == NULL || folder->item == NULL)
 		return FALSE;
 
-	return mp->folder->name != NULL;
+	return folder->item->name != NULL;
 }
 
 static gboolean get_folder_name(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *data)
 {
 	struct media_player *mp = data;
+	struct media_folder *folder = mp->folder;
 
-	if (mp->folder == NULL || mp->folder->name == NULL)
+	if (folder == NULL || folder->item == NULL)
 		return FALSE;
 
-	DBG("%s", mp->folder->name);
+	DBG("%s", folder->item->name);
 
 	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
-							&mp->folder->name);
+							&folder->item->name);
 
 	return TRUE;
 }
@@ -622,14 +634,15 @@ static gboolean get_items(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *data)
 {
 	struct media_player *mp = data;
+	struct media_folder *folder = mp->folder;
 
-	if (mp->folder == NULL)
+	if (folder == NULL)
 		return FALSE;
 
-	DBG("%u", mp->folder->items);
+	DBG("%u", folder->number_of_items);
 
 	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
-							&mp->folder->items);
+						&folder->number_of_items);
 
 	return TRUE;
 }
@@ -657,9 +670,22 @@ static const GDBusPropertyTable media_folder_properties[] = {
 	{ }
 };
 
+static void media_item_destroy(void *data)
+{
+	struct media_item *item = data;
+
+	DBG("%s", item->path);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(), item->path,
+						MEDIA_ITEM_INTERFACE);
+
+	g_free(item->path);
+	g_free(item->name);
+	g_free(item);
+}
+
 static void media_folder_destroy(struct media_folder *folder)
 {
-	g_free(folder->name);
 	g_free(folder);
 }
 
@@ -687,6 +713,7 @@ void media_player_destroy(struct media_player *mp)
 	}
 
 	g_slist_free_full(mp->pending, g_free);
+	g_slist_free_full(mp->folders, media_item_destroy);
 
 	g_timer_destroy(mp->progress);
 	g_free(mp->cb);
@@ -911,31 +938,186 @@ void media_player_set_searchable(struct media_player *mp, bool enabled)
 	mp->searchable = enabled;
 }
 
-void media_player_set_folder(struct media_player *mp, const char *path,
-								uint32_t items)
+static void media_player_set_folder_item(struct media_player *mp,
+						struct media_item *item,
+						uint32_t number_of_items)
 {
 	struct media_folder *folder;
 
-	DBG("%s items %u", path, items);
+	folder = mp->folder;
 
-	if (mp->folder != NULL)
-		media_folder_destroy(mp->folder);
-
-	folder = g_new0(struct media_folder, 1);
-	folder->name = g_strdup(path);
-	folder->items = items;
-	mp->folder = folder;
+	if (folder == NULL) {
+		folder = g_new0(struct media_folder, 1);
+		mp->folder = folder;
 
-	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+		if (!g_dbus_register_interface(btd_get_dbus_connection(),
 					mp->path, MEDIA_FOLDER_INTERFACE,
 					media_folder_methods,
 					NULL,
 					media_folder_properties, mp, NULL)) {
-		error("D-Bus failed to register %s on %s path",
+			error("D-Bus failed to register %s on %s path",
 					MEDIA_FOLDER_INTERFACE, mp->path);
-		media_folder_destroy(mp->folder);
-		mp->folder = NULL;
+			media_folder_destroy(mp->folder);
+			mp->folder = NULL;
+		}
+	}
+
+	folder->item = item;
+	folder->number_of_items = number_of_items;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+				MEDIA_FOLDER_INTERFACE, "Name");
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+				MEDIA_FOLDER_INTERFACE, "NumberOfItems");
+}
+
+static struct media_item *media_player_find_folder(struct media_player *mp,
+							const char *name)
+{
+	GSList *l;
+
+	for (l = mp->folders; l; l = l->next) {
+		struct media_item *item = l->data;
+
+		if (!item->folder)
+			continue;
+
+		if (g_str_equal(item->name, name))
+			return item;
+	}
+
+	return NULL;
+}
+
+void media_player_set_folder(struct media_player *mp, const char *name,
+						uint32_t number_of_items)
+{
+	struct media_item *item;
+
+	DBG("%s number of items %u", name, number_of_items);
+
+	item = media_player_find_folder(mp, name);
+	if (item == NULL) {
+		error("Unknown folder: %s", name);
+		return;
+	}
+
+	media_player_set_folder_item(mp, item, number_of_items);
+}
+
+static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return btd_error_failed(msg, strerror(ENOTSUP));
+}
+
+static DBusMessage *media_item_add_to_nowplaying(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	return btd_error_failed(msg, strerror(ENOTSUP));
+}
+
+static gboolean item_name_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct media_item *item = data;
+
+	return item->name != NULL;
+}
+
+static gboolean get_item_name(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_item *item = data;
+
+	if (item->name == NULL)
+		return FALSE;
+
+	DBG("%s", item->name);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &item->name);
+
+	return TRUE;
+}
+
+static gboolean get_folder_flag(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_item *item = data;
+	dbus_bool_t value;
+
+	DBG("%s", item->folder ? "true" : "false");
+
+	value = item->folder ? TRUE : FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static gboolean get_playable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_item *item = data;
+	dbus_bool_t value;
+
+	DBG("%s", item->playable ? "true" : "false");
+
+	value = item->playable ? TRUE : FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable media_item_methods[] = {
+	{ GDBUS_EXPERIMENTAL_METHOD("Play", NULL, NULL,
+					media_item_play) },
+	{ GDBUS_EXPERIMENTAL_METHOD("AddtoNowPlaying", NULL, NULL,
+					media_item_add_to_nowplaying) },
+	{ }
+};
+
+static const GDBusPropertyTable media_item_properties[] = {
+	{ "Name", "s", get_item_name, NULL, item_name_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Folder", "b", get_folder_flag, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Playable", "b", get_playable, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ }
+};
+
+int media_player_create_folder(struct media_player *mp, const char *name)
+{
+	struct media_item *item;
+
+	DBG("%s", name);
+
+	item = g_new0(struct media_item, 1);
+	item->player = mp;
+	item->path = g_strdup_printf("%s%s", mp->path, name);
+	item->name = g_strdup(name);
+	item->folder = true;
+	item->playable = false;
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					item->path, MEDIA_ITEM_INTERFACE,
+					media_item_methods,
+					NULL,
+					media_item_properties, item, NULL)) {
+		error("D-Bus failed to register %s on %s path",
+					MEDIA_ITEM_INTERFACE, item->path);
+		media_item_destroy(item);
+		return -EINVAL;
 	}
+
+	if (mp->folder == NULL)
+		media_player_set_folder_item(mp, item, 0);
+
+	mp->folders = g_slist_prepend(mp->folders, item);
+
+	return 0;
 }
 
 void media_player_set_callbacks(struct media_player *mp,
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 9109b1f..a59cc66 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -55,6 +55,8 @@ void media_player_set_searchable(struct media_player *mp, bool enabled);
 void media_player_set_folder(struct media_player *mp, const char *path,
 								uint32_t items);
 
+int media_player_create_folder(struct media_player *mp, const char *path);
+
 void media_player_set_callbacks(struct media_player *mp,
 				const struct media_player_callback *cbs,
 				void *user_data);
-- 
1.8.1


^ permalink raw reply related

* [PATCH BlueZ 4/5 v2] AVRCP: Move features to avrcp.c
From: Luiz Augusto von Dentz @ 2013-02-06  9:14 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1360142100-5141-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

avrcp.c should be responsible for specifics of AVRCP not player.c which
is more high level abstraction.
---
 profiles/audio/avrcp.c  | 4 ++++
 profiles/audio/player.c | 8 --------
 profiles/audio/player.h | 1 -
 3 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index cabe2ee..55887bc 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -180,6 +180,7 @@ struct avrcp_player {
 	GSList *sessions;
 	uint16_t id;
 	uint16_t uid_counter;
+	uint8_t *features;
 
 	struct avrcp_player_cb *cb;
 	void *user_data;
@@ -1981,6 +1982,8 @@ static void avrcp_player_parse_features(struct avrcp_player *player,
 {
 	struct media_player *mp = player->user_data;
 
+	player->features = g_memdup(features, 16);
+
 	if (features[7] & 0x08)
 		media_player_set_browsable(mp, true);
 
@@ -2553,6 +2556,7 @@ static void player_destroy(gpointer data)
 		player->destroy(player->user_data);
 
 	g_slist_free(player->sessions);
+	g_free(player->features);
 	g_free(player);
 }
 
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 019ed1f..558aee5 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -69,7 +69,6 @@ struct media_player {
 	char			*subtype;	/* Player subtype */
 	bool			browsable;	/* Player browsing feature */
 	bool			searchable;	/* Player searching feature */
-	uint8_t			*features;	/* Player features */
 	struct media_folder	*folder;	/* Player currenct folder */
 	char			*path;		/* Player object path */
 	GHashTable		*settings;	/* Player settings */
@@ -939,13 +938,6 @@ void media_player_set_folder(struct media_player *mp, const char *path,
 	}
 }
 
-void media_player_set_features(struct media_player *mp, uint64_t *features)
-{
-	DBG("0x%016" PRIx64 "%016" PRIx64, features[0], features[1]);
-
-	memcpy(features, mp->features, sizeof(mp->features));
-}
-
 void media_player_set_callbacks(struct media_player *mp,
 				const struct media_player_callback *cbs,
 				void *user_data)
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index e9a7d1c..9109b1f 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -49,7 +49,6 @@ void media_player_set_metadata(struct media_player *mp, const char *key,
 						void *data, size_t len);
 void media_player_set_type(struct media_player *mp, const char *type);
 void media_player_set_subtype(struct media_player *mp, const char *subtype);
-void media_player_set_features(struct media_player *mp, uint64_t *features);
 void media_player_set_name(struct media_player *mp, const char *name);
 void media_player_set_browsable(struct media_player *mp, bool enabled);
 void media_player_set_searchable(struct media_player *mp, bool enabled);
-- 
1.8.1


^ permalink raw reply related

* [PATCH BlueZ 3/5 v2] media-api: Fix referencing to MediaLibrary instead of MediaFolder
From: Luiz Augusto von Dentz @ 2013-02-06  9:14 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1360142100-5141-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

MediaLibrary was replaced by MediaFolder so doesn't exist anymore.
---
 doc/media-api.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/doc/media-api.txt b/doc/media-api.txt
index db1575f..2656a9e 100644
--- a/doc/media-api.txt
+++ b/doc/media-api.txt
@@ -233,7 +233,7 @@ Properties	string Equalizer [readwrite]
 		boolean Browsable [readonly]
 
 			If present indicates the player can be browsed using
-			MediaLibrary interface.
+			MediaFolder interface.
 
 			Possible values:
 
@@ -248,7 +248,7 @@ Properties	string Equalizer [readwrite]
 		boolean Searchable [readonly]
 
 			If present indicates the player can be searched using
-			MediaLibrary interface.
+			MediaFolder interface.
 
 			Possible values:
 
-- 
1.8.1


^ permalink raw reply related

* [PATCH BlueZ 2/5 v2] AVRCP: Parse browsing and searching features bits
From: Luiz Augusto von Dentz @ 2013-02-06  9:14 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1360142100-5141-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This parses browsing and searching features bits and set the respective
property.
---
v2: Check if folder is available to enable Browsable and Searchable properties

 profiles/audio/avrcp.c  | 18 +++++++++----
 profiles/audio/player.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++-
 profiles/audio/player.h |  2 ++
 3 files changed, 85 insertions(+), 6 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 5967180..cabe2ee 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1976,6 +1976,18 @@ static void avrcp_set_browsed_player(struct avrcp *session,
 				avrcp_set_browsed_player_rsp, session);
 }
 
+static void avrcp_player_parse_features(struct avrcp_player *player,
+							uint8_t *features)
+{
+	struct media_player *mp = player->user_data;
+
+	if (features[7] & 0x08)
+		media_player_set_browsable(mp, true);
+
+	if (features[7] & 0x10)
+		media_player_set_searchable(mp, true);
+}
+
 static void avrcp_parse_media_player_item(struct avrcp *session,
 					uint8_t *operands, uint16_t len)
 {
@@ -1984,7 +1996,6 @@ static void avrcp_parse_media_player_item(struct avrcp *session,
 	uint16_t id;
 	uint32_t subtype;
 	const char *curval, *strval;
-	uint64_t features[2];
 	char name[255];
 
 	if (len < 28)
@@ -2009,10 +2020,7 @@ static void avrcp_parse_media_player_item(struct avrcp *session,
 		avrcp_get_play_status(session);
 	}
 
-	features[0] = bt_get_be64(&operands[8]);
-	features[1] = bt_get_be64(&operands[16]);
-
-	media_player_set_features(mp, features);
+	avrcp_player_parse_features(player, &operands[8]);
 
 	if (operands[26] != 0) {
 		memcpy(name, &operands[27], operands[26]);
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index f875ee7..019ed1f 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -67,7 +67,9 @@ struct media_player {
 	char			*name;		/* Player name */
 	char			*type;		/* Player type */
 	char			*subtype;	/* Player subtype */
-	uint64_t		features[2];	/* Player features */
+	bool			browsable;	/* Player browsing feature */
+	bool			searchable;	/* Player searching feature */
+	uint8_t			*features;	/* Player features */
 	struct media_folder	*folder;	/* Player currenct folder */
 	char			*path;		/* Player object path */
 	GHashTable		*settings;	/* Player settings */
@@ -350,6 +352,53 @@ static gboolean get_subtype(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
+static gboolean browsable_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->folder != NULL;
+}
+
+static gboolean get_browsable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->folder == NULL)
+		return FALSE;
+
+	DBG("%s", mp->browsable ? "true" : "false");
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+							&mp->browsable);
+
+	return TRUE;
+}
+
+static gboolean searchable_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->folder != NULL;
+}
+
+static gboolean get_searchable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->folder == NULL)
+		return FALSE;
+
+	DBG("%s", mp->searchable ? "true" : "false");
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+							&mp->searchable);
+
+	return TRUE;
+}
+
 static DBusMessage *media_player_play(DBusConnection *conn, DBusMessage *msg,
 								void *data)
 {
@@ -510,6 +559,10 @@ static const GDBusPropertyTable media_player_properties[] = {
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ "Device", "s", get_device, NULL, NULL,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Browsable", "b", get_browsable, NULL, browsable_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Searchable", "b", get_searchable, NULL, searchable_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ }
 };
 
@@ -659,6 +712,8 @@ struct media_player *media_player_controller_create(const char *path)
 	mp->track = g_hash_table_new_full(g_str_hash, g_str_equal,
 							g_free, g_free);
 	mp->progress = g_timer_new();
+	mp->browsable = -1;
+	mp->searchable = -1;
 
 	if (!g_dbus_register_interface(btd_get_dbus_connection(),
 					mp->path, MEDIA_PLAYER_INTERFACE,
@@ -843,6 +898,20 @@ void media_player_set_name(struct media_player *mp, const char *name)
 					"Name");
 }
 
+void media_player_set_browsable(struct media_player *mp, bool enabled)
+{
+	DBG("%s", enabled ? "true" : "false");
+
+	mp->browsable = enabled;
+}
+
+void media_player_set_searchable(struct media_player *mp, bool enabled)
+{
+	DBG("%s", enabled ? "true" : "false");
+
+	mp->searchable = enabled;
+}
+
 void media_player_set_folder(struct media_player *mp, const char *path,
 								uint32_t items)
 {
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 1ac9800..e9a7d1c 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -51,6 +51,8 @@ void media_player_set_type(struct media_player *mp, const char *type);
 void media_player_set_subtype(struct media_player *mp, const char *subtype);
 void media_player_set_features(struct media_player *mp, uint64_t *features);
 void media_player_set_name(struct media_player *mp, const char *name);
+void media_player_set_browsable(struct media_player *mp, bool enabled);
+void media_player_set_searchable(struct media_player *mp, bool enabled);
 void media_player_set_folder(struct media_player *mp, const char *path,
 								uint32_t items);
 
-- 
1.8.1


^ permalink raw reply related

* [PATCH BlueZ 1/5 v2] AVRCP: Prefix folder name with /Filesystem
From: Luiz Augusto von Dentz @ 2013-02-06  9:14 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This separate the scopes of the folder as documented in doc/media-api.txt
---
 profiles/audio/avrcp.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 00eeea1..5967180 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1935,9 +1935,10 @@ static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn,
 
 	depth = operands[13];
 
-	folders = g_new0(char *, depth + 1);
+	folders = g_new0(char *, depth + 2);
+	folders[0] = g_strdup("/Filesystem");
 
-	for (i = 14, count = 0; count < depth; count++) {
+	for (i = 14, count = 1; count - 1 < depth; count++) {
 		char *part;
 		uint8_t len;
 
-- 
1.8.1


^ permalink raw reply related

* [PATCH BlueZ 5/5] AVRCP: Create folders for /Filesystem and /NowPlaying
From: Luiz Augusto von Dentz @ 2013-02-06  8:10 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1360138224-29118-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

If browsing is supported create objects representing /Filesystem and
/NowPlaying, these object implement MediaItem interface and can be
passed to ChangeFolder to change the scope.
---
 profiles/audio/avrcp.c  |   7 +-
 profiles/audio/player.c | 232 ++++++++++++++++++++++++++++++++++++++++++------
 profiles/audio/player.h |   2 +
 3 files changed, 215 insertions(+), 26 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 55887bc..3b9aad6 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1984,11 +1984,16 @@ static void avrcp_player_parse_features(struct avrcp_player *player,
 
 	player->features = g_memdup(features, 16);
 
-	if (features[7] & 0x08)
+	if (features[7] & 0x08) {
 		media_player_set_browsable(mp, true);
+		media_player_create_folder(mp, "/Filesystem");
+	}
 
 	if (features[7] & 0x10)
 		media_player_set_searchable(mp, true);
+
+	if (features[8] & 0x02)
+		media_player_create_folder(mp, "/NowPlaying");
 }
 
 static void avrcp_parse_media_player_item(struct avrcp *session,
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index b91f057..aeb97c6 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -45,6 +45,7 @@
 
 #define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
 #define MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
+#define MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
 
 struct player_callback {
 	const struct media_player_callback *cbs;
@@ -57,9 +58,17 @@ struct pending_req {
 	const char *value;
 };
 
+struct media_item {
+	struct media_player	*player;
+	char			*path;		/* Item object path */
+	char			*name;		/* Item name */
+	bool			folder;		/* Item folder flag */
+	bool			playable;	/* Item playable flag */
+};
+
 struct media_folder {
-	char			*name;		/* Folder name */
-	uint32_t		items;		/* Number of items */
+	struct media_item	*item;		/* Folder item */
+	uint32_t		number_of_items;/* Number of items */
 };
 
 struct media_player {
@@ -79,6 +88,7 @@ struct media_player {
 	guint			process_id;
 	struct player_callback	*cb;
 	GSList			*pending;
+	GSList			*folders;
 };
 
 static void append_metadata(void *key, void *value, void *user_data)
@@ -594,25 +604,27 @@ static gboolean folder_name_exists(const GDBusPropertyTable *property,
 								void *data)
 {
 	struct media_player *mp = data;
+	struct media_folder *folder = mp->folder;
 
-	if (mp->folder == NULL)
+	if (folder == NULL || folder->item == NULL)
 		return FALSE;
 
-	return mp->folder->name != NULL;
+	return folder->item->name != NULL;
 }
 
 static gboolean get_folder_name(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *data)
 {
 	struct media_player *mp = data;
+	struct media_folder *folder = mp->folder;
 
-	if (mp->folder == NULL || mp->folder->name == NULL)
+	if (folder == NULL || folder->item == NULL)
 		return FALSE;
 
-	DBG("%s", mp->folder->name);
+	DBG("%s", folder->item->name);
 
 	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
-							&mp->folder->name);
+							&folder->item->name);
 
 	return TRUE;
 }
@@ -628,14 +640,15 @@ static gboolean get_items(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *data)
 {
 	struct media_player *mp = data;
+	struct media_folder *folder = mp->folder;
 
-	if (mp->folder == NULL)
+	if (folder == NULL)
 		return FALSE;
 
-	DBG("%u", mp->folder->items);
+	DBG("%u", folder->number_of_items);
 
 	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
-							&mp->folder->items);
+						&folder->number_of_items);
 
 	return TRUE;
 }
@@ -663,9 +676,22 @@ static const GDBusPropertyTable media_folder_properties[] = {
 	{ }
 };
 
+static void media_item_destroy(void *data)
+{
+	struct media_item *item = data;
+
+	DBG("%s", item->path);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(), item->path,
+						MEDIA_ITEM_INTERFACE);
+
+	g_free(item->path);
+	g_free(item->name);
+	g_free(item);
+}
+
 static void media_folder_destroy(struct media_folder *folder)
 {
-	g_free(folder->name);
 	g_free(folder);
 }
 
@@ -693,6 +719,7 @@ void media_player_destroy(struct media_player *mp)
 	}
 
 	g_slist_free_full(mp->pending, g_free);
+	g_slist_free_full(mp->folders, media_item_destroy);
 
 	g_timer_destroy(mp->progress);
 	g_free(mp->cb);
@@ -917,31 +944,186 @@ void media_player_set_searchable(struct media_player *mp, bool enabled)
 	mp->searchable = enabled;
 }
 
-void media_player_set_folder(struct media_player *mp, const char *path,
-								uint32_t items)
+static void media_player_set_folder_item(struct media_player *mp,
+						struct media_item *item,
+						uint32_t number_of_items)
 {
 	struct media_folder *folder;
 
-	DBG("%s items %u", path, items);
+	folder = mp->folder;
 
-	if (mp->folder != NULL)
-		media_folder_destroy(mp->folder);
-
-	folder = g_new0(struct media_folder, 1);
-	folder->name = g_strdup(path);
-	folder->items = items;
-	mp->folder = folder;
+	if (folder == NULL) {
+		folder = g_new0(struct media_folder, 1);
+		mp->folder = folder;
 
-	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+		if (!g_dbus_register_interface(btd_get_dbus_connection(),
 					mp->path, MEDIA_FOLDER_INTERFACE,
 					media_folder_methods,
 					NULL,
 					media_folder_properties, mp, NULL)) {
-		error("D-Bus failed to register %s on %s path",
+			error("D-Bus failed to register %s on %s path",
 					MEDIA_FOLDER_INTERFACE, mp->path);
-		media_folder_destroy(mp->folder);
-		mp->folder = NULL;
+			media_folder_destroy(mp->folder);
+			mp->folder = NULL;
+		}
+	}
+
+	folder->item = item;
+	folder->number_of_items = number_of_items;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+				MEDIA_FOLDER_INTERFACE, "Name");
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+				MEDIA_FOLDER_INTERFACE, "NumberOfItems");
+}
+
+static struct media_item *media_player_find_folder(struct media_player *mp,
+							const char *name)
+{
+	GSList *l;
+
+	for (l = mp->folders; l; l = l->next) {
+		struct media_item *item = l->data;
+
+		if (!item->folder)
+			continue;
+
+		if (g_str_equal(item->name, name))
+			return item;
+	}
+
+	return NULL;
+}
+
+void media_player_set_folder(struct media_player *mp, const char *name,
+						uint32_t number_of_items)
+{
+	struct media_item *item;
+
+	DBG("%s number of items %u", name, number_of_items);
+
+	item = media_player_find_folder(mp, name);
+	if (item == NULL) {
+		error("Unknown folder: %s", name);
+		return;
+	}
+
+	media_player_set_folder_item(mp, item, number_of_items);
+}
+
+static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return btd_error_failed(msg, strerror(ENOTSUP));
+}
+
+static DBusMessage *media_item_add_to_nowplaying(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	return btd_error_failed(msg, strerror(ENOTSUP));
+}
+
+static gboolean item_name_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct media_item *item = data;
+
+	return item->name != NULL;
+}
+
+static gboolean get_item_name(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_item *item = data;
+
+	if (item->name == NULL)
+		return FALSE;
+
+	DBG("%s", item->name);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &item->name);
+
+	return TRUE;
+}
+
+static gboolean get_folder_flag(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_item *item = data;
+	dbus_bool_t value;
+
+	DBG("%s", item->folder ? "true" : "false");
+
+	value = item->folder ? TRUE : FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static gboolean get_playable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_item *item = data;
+	dbus_bool_t value;
+
+	DBG("%s", item->playable ? "true" : "false");
+
+	value = item->playable ? TRUE : FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable media_item_methods[] = {
+	{ GDBUS_EXPERIMENTAL_METHOD("Play", NULL, NULL,
+					media_item_play) },
+	{ GDBUS_EXPERIMENTAL_METHOD("AddtoNowPlaying", NULL, NULL,
+					media_item_add_to_nowplaying) },
+	{ }
+};
+
+static const GDBusPropertyTable media_item_properties[] = {
+	{ "Name", "s", get_item_name, NULL, item_name_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Folder", "b", get_folder_flag, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Playable", "b", get_playable, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ }
+};
+
+int media_player_create_folder(struct media_player *mp, const char *name)
+{
+	struct media_item *item;
+
+	DBG("%s", name);
+
+	item = g_new0(struct media_item, 1);
+	item->player = mp;
+	item->path = g_strdup_printf("%s%s", mp->path, name);
+	item->name = g_strdup(name);
+	item->folder = true;
+	item->playable = false;
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					item->path, MEDIA_ITEM_INTERFACE,
+					media_item_methods,
+					NULL,
+					media_item_properties, item, NULL)) {
+		error("D-Bus failed to register %s on %s path",
+					MEDIA_ITEM_INTERFACE, item->path);
+		media_item_destroy(item);
+		return -EINVAL;
 	}
+
+	if (mp->folder == NULL)
+		media_player_set_folder_item(mp, item, 0);
+
+	mp->folders = g_slist_prepend(mp->folders, item);
+
+	return 0;
 }
 
 void media_player_set_callbacks(struct media_player *mp,
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 9109b1f..a59cc66 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -55,6 +55,8 @@ void media_player_set_searchable(struct media_player *mp, bool enabled);
 void media_player_set_folder(struct media_player *mp, const char *path,
 								uint32_t items);
 
+int media_player_create_folder(struct media_player *mp, const char *path);
+
 void media_player_set_callbacks(struct media_player *mp,
 				const struct media_player_callback *cbs,
 				void *user_data);
-- 
1.8.1


^ permalink raw reply related

* [PATCH BlueZ 4/5] AVRCP: Move features to avrcp.c
From: Luiz Augusto von Dentz @ 2013-02-06  8:10 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1360138224-29118-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

avrcp.c should be responsible for specifics of AVRCP not player.c which
is more high level abstraction.
---
 profiles/audio/avrcp.c  | 4 ++++
 profiles/audio/player.c | 8 --------
 profiles/audio/player.h | 1 -
 3 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index cabe2ee..55887bc 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -180,6 +180,7 @@ struct avrcp_player {
 	GSList *sessions;
 	uint16_t id;
 	uint16_t uid_counter;
+	uint8_t *features;
 
 	struct avrcp_player_cb *cb;
 	void *user_data;
@@ -1981,6 +1982,8 @@ static void avrcp_player_parse_features(struct avrcp_player *player,
 {
 	struct media_player *mp = player->user_data;
 
+	player->features = g_memdup(features, 16);
+
 	if (features[7] & 0x08)
 		media_player_set_browsable(mp, true);
 
@@ -2553,6 +2556,7 @@ static void player_destroy(gpointer data)
 		player->destroy(player->user_data);
 
 	g_slist_free(player->sessions);
+	g_free(player->features);
 	g_free(player);
 }
 
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index c75354b..b91f057 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -69,7 +69,6 @@ struct media_player {
 	char			*subtype;	/* Player subtype */
 	dbus_bool_t		browsable;	/* Player browsing feature */
 	dbus_bool_t		searchable;	/* Player searching feature */
-	uint8_t			*features;	/* Player features */
 	struct media_folder	*folder;	/* Player currenct folder */
 	char			*path;		/* Player object path */
 	GHashTable		*settings;	/* Player settings */
@@ -945,13 +944,6 @@ void media_player_set_folder(struct media_player *mp, const char *path,
 	}
 }
 
-void media_player_set_features(struct media_player *mp, uint64_t *features)
-{
-	DBG("0x%016" PRIx64 "%016" PRIx64, features[0], features[1]);
-
-	memcpy(features, mp->features, sizeof(mp->features));
-}
-
 void media_player_set_callbacks(struct media_player *mp,
 				const struct media_player_callback *cbs,
 				void *user_data)
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index e9a7d1c..9109b1f 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -49,7 +49,6 @@ void media_player_set_metadata(struct media_player *mp, const char *key,
 						void *data, size_t len);
 void media_player_set_type(struct media_player *mp, const char *type);
 void media_player_set_subtype(struct media_player *mp, const char *subtype);
-void media_player_set_features(struct media_player *mp, uint64_t *features);
 void media_player_set_name(struct media_player *mp, const char *name);
 void media_player_set_browsable(struct media_player *mp, bool enabled);
 void media_player_set_searchable(struct media_player *mp, bool enabled);
-- 
1.8.1


^ permalink raw reply related

* [PATCH BlueZ 3/5] media-api: Fix referencing to MediaLibrary instead of MediaFolder
From: Luiz Augusto von Dentz @ 2013-02-06  8:10 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1360138224-29118-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

MediaLibrary was replaced by MediaFolder so doesn't exist anymore.
---
 doc/media-api.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/doc/media-api.txt b/doc/media-api.txt
index db1575f..2656a9e 100644
--- a/doc/media-api.txt
+++ b/doc/media-api.txt
@@ -233,7 +233,7 @@ Properties	string Equalizer [readwrite]
 		boolean Browsable [readonly]
 
 			If present indicates the player can be browsed using
-			MediaLibrary interface.
+			MediaFolder interface.
 
 			Possible values:
 
@@ -248,7 +248,7 @@ Properties	string Equalizer [readwrite]
 		boolean Searchable [readonly]
 
 			If present indicates the player can be searched using
-			MediaLibrary interface.
+			MediaFolder interface.
 
 			Possible values:
 
-- 
1.8.1


^ permalink raw reply related

* [PATCH BlueZ 2/5] AVRCP: Parse browsing and searching features bits
From: Luiz Augusto von Dentz @ 2013-02-06  8:10 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1360138224-29118-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This parses browsing and searching features bits and set the respective
property.
---
 profiles/audio/avrcp.c  | 18 ++++++++----
 profiles/audio/player.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++-
 profiles/audio/player.h |  2 ++
 3 files changed, 91 insertions(+), 6 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 5967180..cabe2ee 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1976,6 +1976,18 @@ static void avrcp_set_browsed_player(struct avrcp *session,
 				avrcp_set_browsed_player_rsp, session);
 }
 
+static void avrcp_player_parse_features(struct avrcp_player *player,
+							uint8_t *features)
+{
+	struct media_player *mp = player->user_data;
+
+	if (features[7] & 0x08)
+		media_player_set_browsable(mp, true);
+
+	if (features[7] & 0x10)
+		media_player_set_searchable(mp, true);
+}
+
 static void avrcp_parse_media_player_item(struct avrcp *session,
 					uint8_t *operands, uint16_t len)
 {
@@ -1984,7 +1996,6 @@ static void avrcp_parse_media_player_item(struct avrcp *session,
 	uint16_t id;
 	uint32_t subtype;
 	const char *curval, *strval;
-	uint64_t features[2];
 	char name[255];
 
 	if (len < 28)
@@ -2009,10 +2020,7 @@ static void avrcp_parse_media_player_item(struct avrcp *session,
 		avrcp_get_play_status(session);
 	}
 
-	features[0] = bt_get_be64(&operands[8]);
-	features[1] = bt_get_be64(&operands[16]);
-
-	media_player_set_features(mp, features);
+	avrcp_player_parse_features(player, &operands[8]);
 
 	if (operands[26] != 0) {
 		memcpy(name, &operands[27], operands[26]);
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index f875ee7..c75354b 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -67,7 +67,9 @@ struct media_player {
 	char			*name;		/* Player name */
 	char			*type;		/* Player type */
 	char			*subtype;	/* Player subtype */
-	uint64_t		features[2];	/* Player features */
+	dbus_bool_t		browsable;	/* Player browsing feature */
+	dbus_bool_t		searchable;	/* Player searching feature */
+	uint8_t			*features;	/* Player features */
 	struct media_folder	*folder;	/* Player currenct folder */
 	char			*path;		/* Player object path */
 	GHashTable		*settings;	/* Player settings */
@@ -350,6 +352,59 @@ static gboolean get_subtype(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
+static gboolean browsable_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->browsable != FALSE && mp->browsable != TRUE)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean get_browsable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->browsable != FALSE && mp->browsable != TRUE)
+		return FALSE;
+
+	DBG("%s", mp->browsable ? "true" : "false");
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+							&mp->browsable);
+
+	return TRUE;
+}
+
+static gboolean searchable_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->searchable != FALSE && mp->searchable != TRUE)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean get_searchable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->searchable != FALSE && mp->searchable != TRUE)
+		return FALSE;
+
+	DBG("%s", mp->searchable ? "true" : "false");
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+							&mp->searchable);
+
+	return TRUE;
+}
+
 static DBusMessage *media_player_play(DBusConnection *conn, DBusMessage *msg,
 								void *data)
 {
@@ -510,6 +565,10 @@ static const GDBusPropertyTable media_player_properties[] = {
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ "Device", "s", get_device, NULL, NULL,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Browsable", "b", get_browsable, NULL, browsable_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Searchable", "b", get_searchable, NULL, searchable_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ }
 };
 
@@ -659,6 +718,8 @@ struct media_player *media_player_controller_create(const char *path)
 	mp->track = g_hash_table_new_full(g_str_hash, g_str_equal,
 							g_free, g_free);
 	mp->progress = g_timer_new();
+	mp->browsable = -1;
+	mp->searchable = -1;
 
 	if (!g_dbus_register_interface(btd_get_dbus_connection(),
 					mp->path, MEDIA_PLAYER_INTERFACE,
@@ -843,6 +904,20 @@ void media_player_set_name(struct media_player *mp, const char *name)
 					"Name");
 }
 
+void media_player_set_browsable(struct media_player *mp, bool enabled)
+{
+	DBG("%s", enabled ? "true" : "false");
+
+	mp->browsable = enabled;
+}
+
+void media_player_set_searchable(struct media_player *mp, bool enabled)
+{
+	DBG("%s", enabled ? "true" : "false");
+
+	mp->searchable = enabled;
+}
+
 void media_player_set_folder(struct media_player *mp, const char *path,
 								uint32_t items)
 {
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 1ac9800..e9a7d1c 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -51,6 +51,8 @@ void media_player_set_type(struct media_player *mp, const char *type);
 void media_player_set_subtype(struct media_player *mp, const char *subtype);
 void media_player_set_features(struct media_player *mp, uint64_t *features);
 void media_player_set_name(struct media_player *mp, const char *name);
+void media_player_set_browsable(struct media_player *mp, bool enabled);
+void media_player_set_searchable(struct media_player *mp, bool enabled);
 void media_player_set_folder(struct media_player *mp, const char *path,
 								uint32_t items);
 
-- 
1.8.1


^ permalink raw reply related

* [PATCH BlueZ 1/5] AVRCP: Prefix folder name with /Filesystem
From: Luiz Augusto von Dentz @ 2013-02-06  8:10 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This separate the scopes of the folder as documented in doc/media-api.txt
---
 profiles/audio/avrcp.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 00eeea1..5967180 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1935,9 +1935,10 @@ static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn,
 
 	depth = operands[13];
 
-	folders = g_new0(char *, depth + 1);
+	folders = g_new0(char *, depth + 2);
+	folders[0] = g_strdup("/Filesystem");
 
-	for (i = 14, count = 0; count < depth; count++) {
+	for (i = 14, count = 1; count - 1 < depth; count++) {
 		char *part;
 		uint8_t len;
 
-- 
1.8.1


^ permalink raw reply related

* Re: [PATCH v0] core: Fix racy D-Bus service name request
From: Luiz Augusto von Dentz @ 2013-02-06  8:08 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: Mikel Astiz, linux-bluetooth@vger.kernel.org, Mikel Astiz
In-Reply-To: <1360136399.15783.1.camel@aeonflux>

Hi Marcel, Mikel,

On Wed, Feb 6, 2013 at 9:39 AM, Marcel Holtmann <marcel@holtmann.org> wrote:
> Hi Mikel,
>
>> The ObjectManager interface should already be ready by the time the
>> D-Bus service name is owned. Hence, use g_dbus_request_name() explicitly
>> once g_dbus_attach_object_manager() has finished.
>>
>> Without this patch, clients relying on the presence of the ObjectManager
>> could potentially face a race condition.
>
> I almost expected that someone sooner or later will run into this issue.
>
>> ---
>>  src/main.c | 8 +++++++-
>>  1 file changed, 7 insertions(+), 1 deletion(-)
>>
>> diff --git a/src/main.c b/src/main.c
>> index 1e40ebc..12ae3d9 100644
>> --- a/src/main.c
>> +++ b/src/main.c
>> @@ -424,7 +424,7 @@ static int connect_dbus(void)
>>
>>       dbus_error_init(&err);
>>
>> -     conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, BLUEZ_NAME, &err);
>> +     conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err);
>>       if (!conn) {
>>               if (dbus_error_is_set(&err)) {
>>                       g_printerr("D-Bus setup failed: %s\n", err.message);
>> @@ -439,6 +439,12 @@ static int connect_dbus(void)
>>       g_dbus_set_disconnect_function(conn, disconnected_dbus, NULL, NULL);
>>       g_dbus_attach_object_manager(conn);
>>
>> +     if (!g_dbus_request_name(conn, BLUEZ_NAME, &err)) {
>> +             g_printerr("D-Bus service name request failed: %s\n",
>> +                                                             err.message);
>> +             return -EIO;
>> +     }
>> +
>
> The only thing that I do not really like here is that the request name
> is actually a blocking call. If we are trying to fix this, then we might
> actually really do this async.

But still the problem seems to be that we should attach object manager
before requesting the name, so perhaps what we should do is to change
g_dbus_setup_bus or create another function that does take into
account object manager e.g. g_dbus_setup_bus_with_object_manager. I
agree with the async name request though and that would cause
g_dbus_setup_bus to change anyway because we probably need a callback
to tell when it is complete.

--
Luiz Augusto von Dentz

^ permalink raw reply

* Re: [PATCH 2/2] adapter: Set default adapter id sooner
From: Marcel Holtmann @ 2013-02-06  7:57 UTC (permalink / raw)
  To: Szymon Janc; +Cc: linux-bluetooth
In-Reply-To: <1360098656-12488-2-git-send-email-szymon@janc.net.pl>

Hi Szymon,

> When first adapter is registered set it as default before probing
> drivers or profiles as those might depend on that e.g. hostname.
> 
> This fix hostname plugin setting default adapter name to 'foo #1'
> instead of 'foo' if pretty hostname was received before probing
> adapter drivers.

you find an issue that I meant to fix, but never got around so far. So I
do not like the magic default_adapter_id global variable.

I would prefer if we add a is_default or similar variable to btd_adapter
and make it change if one adapter goes away.

Regards

Marcel



^ permalink raw reply


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