Subject: [PATCH 00/32] Health Device Prifile (HDP) -- updated

Next patches, that Santiago Carot-Nemesio and I have developed, are applied
over the MCAP patches previously sent to the mailing list.

This a first patches manage the creation of links beetween instances and
begins the support for creating data chanels, but doesn't finish it yet.

Also new debug features are used.

You could find a branch with all the patches (MCAP/HDP) applied here:

git://gitorious.org/bluez-mcap-hdp/bluez-mcap-hdp.git

Regards



Subject: [PATCH 04/32] Initial support for hdp_device_drivers

From: José Antonio Santos Cadenas <[email protected]>

---
health/hdp.c | 11 +++++++++++
health/hdp.h | 3 +++
health/manager.c | 10 +++++++---
3 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 454483d..dbe326e 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -184,3 +184,14 @@ void hdp_adapter_unregister(struct btd_adapter *btd_adapter)

DBG("HDP exit");
}
+
+int health_device_register(struct btd_device *device, const char *uuid)
+{
+ DBG("HDP_DRIVER_PROBE with uuid %s", uuid);
+ return 0;
+}
+
+void health_device_unregister(struct btd_device *device)
+{
+ DBG("TODO: Remove device");
+}
diff --git a/health/hdp.h b/health/hdp.h
index 893f745..0aae7b9 100644
--- a/health/hdp.h
+++ b/health/hdp.h
@@ -25,3 +25,6 @@

int hdp_adapter_register(DBusConnection *conn, struct btd_adapter *btd_adapter);
void hdp_adapter_unregister(struct btd_adapter *btd_adapter);
+
+int health_device_register(struct btd_device *device, const char *uuid);
+void health_device_unregister(struct btd_device *device);
diff --git a/health/manager.c b/health/manager.c
index dc354f8..d163545 100644
--- a/health/manager.c
+++ b/health/manager.c
@@ -63,17 +63,21 @@ static struct btd_adapter_driver hdp_adapter_driver = {

static int hdp_driver_probe(struct btd_device *device, GSList *uuids)
{
- DBG("hdp driver probe");
+ while (uuids) {
+ health_device_register(device, uuids->data);
+ uuids = uuids->next;
+ }
+
return 0;
}

static void hdp_driver_remove(struct btd_device *device)
{
- DBG("hdp driver remove");
+ health_device_unregister(device);
}

static struct btd_device_driver hdp_device_driver = {
- .name = "hdp_device-driver",
+ .name = "hdp-device-driver",
.uuids = BTD_UUIDS(HDP_UUID, HDP_SOURCE_UUID, HDP_SINK_UUID),
.probe = hdp_driver_probe,
.remove = hdp_driver_remove,
--
1.6.3.3


Subject: [PATCH 06/32] Add delete instance petition

---
health/hdp.c | 64 +++++++++++++++++++++++++++++++++++++++++++----------
health/hdp_util.c | 35 +++++++++++++++++++++++++++-
2 files changed, 85 insertions(+), 14 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 5b09ab3..8d90588 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -34,7 +34,6 @@
#include "../src/dbus-common.h"

#define HEALTH_MANAGER_INTERFACE "org.bluez.HealthAdapter"
-#define HEALTH_INSTANCE_INTERFACE "org.bluez.HealthInstance"
#define HEALTH_DEVICE "org.bluez.HealthDevice"

static GSList *adapters = NULL;
@@ -147,6 +146,16 @@ static struct hdp_device *create_health_device(DBusConnection *conn,
return dev;
}

+static int hdp_instance_idcmp(gconstpointer instance, gconstpointer p)
+{
+ const struct hdp_instance *hdpi = instance;
+ const uint32_t *id = p;
+
+ if (hdpi->id == *id)
+ return 0;
+ return -1;
+}
+
static void hdp_set_instance_id(struct hdp_instance *hdpi)
{
struct hdp_adapter *adapter = hdpi->adapter;
@@ -187,24 +196,20 @@ static DBusMessage *hdp_create_instance(DBusConnection *conn,
hdpi->aname = g_strdup(name);
hdpi->apath = g_strdup(path);
hdpi->config = config;
- if (!config->svc_dsc)
- config->svc_dsc = g_strdup(HDP_SERVICE_DSC);
- if (!config->svc_name)
- config->svc_name = g_strdup(HDP_SERVICE_NAME);
- if (!config->svc_prov)
- config->svc_prov = g_strdup(HDP_SERVICE_PROVIDER);
hdp_set_instance_id(hdpi);

/* TODO: Create mcap instance */

if (!hdp_register_sdp_record(hdpi)) {
+ hdp_instance_free(hdpi);
return g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
"Session can't be registered");
}

- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".HealthError",
- "Incomplete call yet");
+ adapter->instances = g_slist_prepend(adapter->instances, hdpi);
+ info("HDP instance created with path %d", hdpi->id);
+ return g_dbus_create_reply(msg, DBUS_TYPE_UINT32, &hdpi->id,
+ DBUS_TYPE_INVALID);
error:
if (err) {
reply = g_dbus_create_error(msg,
@@ -218,16 +223,51 @@ error:
return reply;
}

+static DBusMessage *hdp_delete_instance(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct hdp_adapter *adapter = user_data;
+ struct hdp_instance *hdpi;
+ GSList *l;
+ const char *name;
+ uint32_t id;
+
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &id,
+ DBUS_TYPE_INVALID))
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+
+ l = g_slist_find_custom(adapter->instances, &id, hdp_instance_idcmp);
+ if (!l)
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".NotFound",
+ "The session was not found");
+
+ name = dbus_message_get_sender(msg);
+ hdpi = l->data;
+ if (g_strcmp0(hdpi->aname, name) != 0)
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "This session was created by an other process");
+ adapter->instances = g_slist_remove(adapter->instances, hdpi);
+ hdp_instance_free(hdpi);
+
+ DBG("Stop HDP Session %d deleted", id);
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
static GDBusMethodTable hdp_methods[] = {
{ "CreateInstance", "oa{sv}", "u", hdp_create_instance },
+ { "DeleteInstance", "u", "", hdp_delete_instance },
{ NULL }
};

void hdp_delete_instance_iter(gpointer data, gpointer user_data)
{
- /* struct hdp_instance *hdpi = data; */
+ struct hdp_instance *hdpi = data;

- /* TODO: Create a free function */
+ hdp_instance_free(hdpi);
}

static void hdp_path_unregister(void *data)
diff --git a/health/hdp_util.c b/health/hdp_util.c
index 96fa737..31a0248 100644
--- a/health/hdp_util.c
+++ b/health/hdp_util.c
@@ -109,6 +109,28 @@ static void free_config(struct hdp_config *config)
g_free(config);
}

+void hdp_instance_free(struct hdp_instance *hdpi)
+{
+ DBG("HDP instance %d is deleted", hdpi->id);
+ /* TODO: Complete this part */
+ /*
+ g_slist_foreach(hdpi->devices, hdp_device_unregister, NULL);
+ g_slist_free(hdpi->devices);
+ hdpi->devices = NULL;
+ */
+
+ if (hdpi->sdp_handler)
+ remove_record_from_server(hdpi->sdp_handler);
+ /* TODO: stop mcap instance */
+ if (hdpi->apath)
+ g_free(hdpi->apath);
+ if (hdpi->aname)
+ g_free(hdpi->aname);
+ if (hdpi->config)
+ free_config(hdpi->config);
+ g_free(hdpi);
+}
+
static gboolean parse_dict_entry(struct dict_entry_func dict_context[],
DBusMessageIter *iter,
GError **err,
@@ -441,8 +463,17 @@ struct hdp_config *hdp_get_config(DBusMessageIter *iter, GError **err)
"\"data_spec\" and \"end_point\" should be set or not");
goto error;
}
- if (!config->ds_present)
- goto error;
+ if (!config->ds_present) {
+ g_free(config);
+ return NULL;
+ }
+ if (!config->svc_dsc)
+ config->svc_dsc = g_strdup(HDP_SERVICE_DSC);
+ if (!config->svc_name)
+ config->svc_name = g_strdup(HDP_SERVICE_NAME);
+ if (!config->svc_prov)
+ config->svc_prov = g_strdup(HDP_SERVICE_PROVIDER);
+
DBG("config->data_spec %d", config->data_spec);
g_slist_foreach(config->supp_fts, print_features, NULL);
return config;
--
1.6.3.3


Subject: [PATCH 07/32] Add watcher to control client disconections to delete hdp instance

---
health/hdp.c | 11 +++++++++++
health/hdp_types.h | 1 +
health/hdp_util.c | 2 ++
3 files changed, 14 insertions(+), 0 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 8d90588..31abb4c 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -163,6 +163,15 @@ static void hdp_set_instance_id(struct hdp_instance *hdpi)
hdpi->id = adapter->ic++;
}

+static void client_disconnected(DBusConnection *connection, void *user_data)
+{
+ struct hdp_instance *hdpi = user_data;
+ struct hdp_adapter *adapter = hdpi->adapter;
+ DBG("Client disconnected from the bus, deleting hdp instance");
+ adapter->instances = g_slist_remove(adapter->instances, hdpi);
+ hdp_instance_free(hdpi);
+}
+
static DBusMessage *hdp_create_instance(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
@@ -197,6 +206,8 @@ static DBusMessage *hdp_create_instance(DBusConnection *conn,
hdpi->apath = g_strdup(path);
hdpi->config = config;
hdp_set_instance_id(hdpi);
+ hdpi->dbus_watcher = g_dbus_add_disconnect_watch(adapter->conn, name,
+ client_disconnected, hdpi, NULL);

/* TODO: Create mcap instance */

diff --git a/health/hdp_types.h b/health/hdp_types.h
index 171910a..3bab4ea 100644
--- a/health/hdp_types.h
+++ b/health/hdp_types.h
@@ -98,6 +98,7 @@ struct hdp_instance {
char *aname; /* HDP agent name */
struct hdp_config *config; /* Configuration */
uint32_t sdp_handler; /* SDP record handler */
+ guint dbus_watcher; /* Client D-Bus conn watcher */
};

struct hdp_device {
diff --git a/health/hdp_util.c b/health/hdp_util.c
index 31a0248..5397f11 100644
--- a/health/hdp_util.c
+++ b/health/hdp_util.c
@@ -119,6 +119,8 @@ void hdp_instance_free(struct hdp_instance *hdpi)
hdpi->devices = NULL;
*/

+ if (hdpi->dbus_watcher)
+ g_dbus_remove_watch(hdpi->adapter->conn, hdpi->dbus_watcher);
if (hdpi->sdp_handler)
remove_record_from_server(hdpi->sdp_handler);
/* TODO: stop mcap instance */
--
1.6.3.3


Subject: [PATCH 03/32] Add functions to resiger health instances in SDP

---
health/hdp.c | 5 +-
health/hdp_util.c | 393 +++++++++++++++++++++++++++++++++++++++++++++++++++++
health/hdp_util.h | 1 +
3 files changed, 398 insertions(+), 1 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 2dec069..454483d 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -101,7 +101,10 @@ static DBusMessage *hdp_create_instance(DBusConnection *conn,

/* TODO: Create mcap instance */

- /* TODO: Create SDP record if needed. */
+ if (!hdp_register_sdp_record(hdpi)) {
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "Session can't be registered");
+ }

return g_dbus_create_error(msg,
ERROR_INTERFACE ".HealthError",
diff --git a/health/hdp_util.c b/health/hdp_util.c
index a215d5a..cc5dc2e 100644
--- a/health/hdp_util.c
+++ b/health/hdp_util.c
@@ -24,8 +24,14 @@
*/

#include <gdbus.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
#include "log.h"
+#include "sdpd.h"
+
#include "hdp_types.h"
+#include "hdp_util.h"
+#include "mcap.h"

typedef gboolean (*parse_item_f)(DBusMessageIter *iter, GError **err,
gpointer user_data);
@@ -445,3 +451,390 @@ error:
free_config(config);
return NULL;
}
+
+static gboolean is_session_role(struct hdp_instance *hdps, HdpRole role)
+{
+ GSList *l;
+ struct hdp_supp_fts *fts;
+
+ if (!hdps->config)
+ return FALSE;
+ for (l = hdps->config->supp_fts; l; l = l->next) {
+ fts = l->data;
+ if (fts->role == role)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean register_service_protocols(struct hdp_instance *hdps,
+ sdp_record_t *sdp_record)
+{
+ gboolean error = FALSE;
+ uuid_t l2cap_uuid, mcap_c_uuid;
+ sdp_list_t *l2cap_list = NULL,
+ *proto_list = NULL,
+ *mcap_list = NULL,
+ *access_proto_list = NULL;
+ sdp_data_t *psm = NULL,
+ *mcap_ver = NULL;
+ uint16_t version = MCAP_VERSION;
+
+ // set l2cap information
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+ if (!l2cap_list) {
+ error = TRUE;
+ goto end;
+ }
+ psm = sdp_data_alloc(SDP_UINT16, &hdps->ccpsm);
+ if (!psm) {
+ error = TRUE;
+ goto end;
+ }
+ if (!sdp_list_append(l2cap_list, psm)) {
+ error = TRUE;
+ goto end;
+ }
+ proto_list = sdp_list_append(NULL, l2cap_list);
+ if (!proto_list) {
+ error = TRUE;
+ goto end;
+ }
+
+ // set mcap information
+ sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID);
+ mcap_list = sdp_list_append(NULL, &mcap_c_uuid);
+ if (!mcap_list) {
+ error = TRUE;
+ goto end;
+ }
+ mcap_ver = sdp_data_alloc(SDP_UINT16, &version);
+ if (!mcap_ver) {
+ error = TRUE;
+ goto end;
+ }
+ if (!sdp_list_append( mcap_list, mcap_ver)) {
+ error = TRUE;
+ goto end;
+ }
+ if (!sdp_list_append( proto_list, mcap_list)) {
+ error = TRUE;
+ goto end;
+ }
+
+ // attach protocol information to service record
+ access_proto_list = sdp_list_append(NULL, proto_list);
+ if (!access_proto_list) {
+ error = TRUE;
+ goto end;
+ }
+ if (sdp_set_access_protos(sdp_record, access_proto_list) < 0)
+ error = TRUE;
+end:
+ if (l2cap_list)
+ sdp_list_free(l2cap_list, NULL);
+ if (mcap_list)
+ sdp_list_free(mcap_list, NULL);
+ if (proto_list)
+ sdp_list_free(proto_list, NULL);
+ if (access_proto_list)
+ sdp_list_free(access_proto_list, NULL);
+ if (psm)
+ sdp_data_free(psm);
+ if (mcap_ver)
+ sdp_data_free(mcap_ver);
+ return !error;
+}
+
+static gboolean register_service_profiles(sdp_record_t *sdp_record)
+{
+ gboolean error = FALSE;
+ sdp_list_t *profile_list = NULL;
+ sdp_profile_desc_t hdp_profile;
+
+ // set hdp information
+ sdp_uuid16_create( &hdp_profile.uuid, MDP_SVCLASS_ID);
+ hdp_profile.version = HDP_VERSION;
+ profile_list = sdp_list_append(NULL, &hdp_profile);
+ if (!profile_list)
+ return FALSE;
+ // set profile descriptor list
+ if (sdp_set_profile_descs(sdp_record, profile_list) < 0)
+ error = TRUE;
+
+ sdp_list_free(profile_list, NULL);
+ return !error;
+}
+
+static gboolean register_service_aditional_protocols(struct hdp_instance *hdps,
+ sdp_record_t *sdp_record)
+{
+ gboolean error = FALSE;
+ uuid_t l2cap_uuid, mcap_d_uuid;
+ sdp_list_t *l2cap_list = NULL,
+ *proto_list = NULL,
+ *mcap_list = NULL,
+ *access_proto_list = NULL;
+ sdp_data_t *psm = NULL;
+
+ // set l2cap information
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+ if (!l2cap_list) {
+ error = TRUE;
+ goto end;
+ }
+ psm = sdp_data_alloc(SDP_UINT16, &hdps->dcpsm);
+ if (!psm) {
+ error = TRUE;
+ goto end;
+ }
+ if (!sdp_list_append(l2cap_list, psm)) {
+ error = TRUE;
+ goto end;
+ }
+ proto_list = sdp_list_append(NULL, l2cap_list);
+ if (!proto_list) {
+ error = TRUE;
+ goto end;
+ }
+
+ // set mcap information
+ sdp_uuid16_create(&mcap_d_uuid, MCAP_DATA_UUID);
+ mcap_list = sdp_list_append(NULL, &mcap_d_uuid);
+ if (!mcap_list) {
+ error = TRUE;
+ goto end;
+ }
+ if (!sdp_list_append( proto_list, mcap_list)) {
+ error = TRUE;
+ goto end;
+ }
+
+ // attach protocol information to service record
+ access_proto_list = sdp_list_append(NULL, proto_list);
+ if (!access_proto_list) {
+ error = TRUE;
+ goto end;
+ }
+ if (sdp_set_add_access_protos(sdp_record, access_proto_list) < 0)
+ error = TRUE;
+end:
+ if (l2cap_list)
+ sdp_list_free(l2cap_list, NULL);
+ if (mcap_list)
+ sdp_list_free(mcap_list, NULL);
+ if (proto_list)
+ sdp_list_free(proto_list, NULL);
+ if (access_proto_list)
+ sdp_list_free(access_proto_list, NULL);
+ if (psm)
+ sdp_data_free(psm);
+ return !error;
+}
+
+static sdp_list_t *feature_to_sdplist(struct hdp_supp_fts *fts,
+ struct hdp_feature *f)
+{
+ sdp_data_t *mdepid,
+ *dtype = NULL,
+ *role = NULL,
+ *desc = NULL;
+ sdp_list_t *f_list = NULL;
+
+ mdepid = sdp_data_alloc(SDP_UINT8, &fts->mdepid);
+ if (!mdepid)
+ return NULL;
+ dtype = sdp_data_alloc(SDP_UINT16, &f->dtype);
+ if (!dtype)
+ goto error;
+ role = sdp_data_alloc(SDP_UINT8, &fts->role);
+ if (!role)
+ goto error;
+ if (f->dscr) {
+ desc = sdp_data_alloc(SDP_TEXT_STR8, f->dscr);
+ if (!desc)
+ goto error;
+ }
+ f_list = sdp_list_append(NULL, mdepid);
+ if (!f_list)
+ goto error;
+ if (!sdp_list_append(f_list, dtype))
+ goto error;
+ if (!sdp_list_append(f_list, role))
+ goto error;
+ if (desc)
+ if (!sdp_list_append(f_list, desc))
+ goto error;
+ return f_list;
+error:
+ if (f_list)
+ sdp_list_free(f_list, NULL);
+ if (mdepid)
+ sdp_data_free(mdepid);
+ if (dtype)
+ sdp_data_free(dtype);
+ if (role)
+ sdp_data_free(role);
+ if (desc)
+ sdp_data_free(desc);
+ return NULL;
+}
+
+static gboolean register_features(struct hdp_supp_fts *fts,
+ sdp_list_t **sup_features)
+{
+ GSList *l;
+ sdp_list_t *hdp_feature = NULL;
+
+ for (l = fts->features; l; l = l->next){
+ hdp_feature = feature_to_sdplist(fts, l->data);
+ if (!hdp_feature)
+ goto error;
+
+ if (!*sup_features) {
+ *sup_features = sdp_list_append(NULL, hdp_feature);
+ if (!*sup_features)
+ goto error;
+ } else if (!sdp_list_append(*sup_features, hdp_feature))
+ goto error;
+ hdp_feature = NULL;
+ }
+ return TRUE;
+error:
+ if (hdp_feature)
+ sdp_list_free(hdp_feature, (sdp_free_func_t)sdp_data_free);
+ return FALSE;
+}
+
+static void free_hdp_list(void *list)
+{
+ sdp_list_t *hdp_list = list;
+
+ sdp_list_free(hdp_list, (sdp_free_func_t)sdp_data_free);
+}
+
+static gboolean register_service_sup_features(struct hdp_config *config,
+ sdp_record_t *sdp_record)
+{
+ GSList *l;
+ sdp_list_t *sup_features = NULL;
+ for (l = config->supp_fts; l; l = l->next) {
+ if (!register_features(l->data, &sup_features))
+ return FALSE;
+ }
+ if (sdp_set_supp_feat(sdp_record, sup_features) < 0) {
+ sdp_list_free(sup_features, free_hdp_list);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean register_data_exchange_spec(struct hdp_config *config,
+ sdp_record_t *record)
+{
+ sdp_data_t *spec;
+
+ spec = sdp_data_alloc(SDP_UINT8, &config->data_spec);
+ if (!spec)
+ return FALSE;
+ if (sdp_attr_add(record, SDP_ATTR_DATA_EXCHANGE_SPEC, spec) < 0) {
+ sdp_data_free(spec);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean register_mcap_features(sdp_record_t *sdp_record)
+{
+ sdp_data_t *mcap_proc;
+ uint8_t mcap_sup_proc = MCAP_SUP_PROC;
+
+ mcap_proc = sdp_data_alloc(SDP_UINT8, &mcap_sup_proc);
+ if (!mcap_proc)
+ return FALSE;
+ if (sdp_attr_add(sdp_record, SDP_ATTR_MCAP_SUPPORTED_PROCEDURES,
+ mcap_proc) < 0) {
+ sdp_data_free(mcap_proc);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean set_sdp_services_uuid(sdp_record_t *record, HdpRole role)
+{
+ uuid_t svc_uuid_source, svc_uuid_sink;
+ sdp_list_t *svc_list = NULL;
+
+ sdp_uuid16_create(&svc_uuid_sink, MDP_SINK_SVCLASS_ID);
+ sdp_uuid16_create(&svc_uuid_source, MDP_SOURCE_SVCLASS_ID);
+
+ sdp_get_service_classes(record, &svc_list);
+
+ if (role == HDP_SOURCE) {
+ if (sdp_list_find(svc_list, &svc_uuid_source, sdp_uuid_cmp) == NULL)
+ svc_list = sdp_list_append(svc_list, &svc_uuid_source);
+ }
+ else if (role == HDP_SINK) {
+ if (sdp_list_find(svc_list, &svc_uuid_sink, sdp_uuid_cmp) == NULL)
+ svc_list = sdp_list_append(svc_list, &svc_uuid_sink);
+ }
+
+ if (sdp_set_service_classes(record, svc_list) < 0) {
+ sdp_list_free(svc_list, NULL);
+ return FALSE;
+ }
+
+ sdp_list_free(svc_list, NULL);
+ return TRUE;
+}
+
+gboolean hdp_register_sdp_record(struct hdp_instance *hdps)
+{
+ sdp_record_t *sdp_record;
+ struct hdp_config *config;
+ bdaddr_t addr;
+
+ if (!hdps->config) /* Record is not needed */
+ return TRUE;
+ config = hdps->config;
+
+ sdp_record = sdp_record_alloc();
+ if (!sdp_record)
+ return FALSE;
+ sdp_record->handle = 0xffffffff; /* Set automatically */
+
+ if (is_session_role(hdps, HDP_SINK))
+ set_sdp_services_uuid(sdp_record, HDP_SINK);
+ if (is_session_role(hdps, HDP_SOURCE))
+ set_sdp_services_uuid(sdp_record, HDP_SOURCE);
+
+ if (!register_service_protocols(hdps, sdp_record))
+ goto error;
+ if (!register_service_profiles(sdp_record))
+ goto error;
+ if (!register_service_aditional_protocols(hdps, sdp_record))
+ goto error;
+ sdp_set_info_attr(sdp_record, config->svc_name, config->svc_prov,
+ config->svc_dsc);
+ if (!register_service_sup_features(config, sdp_record))
+ goto error;
+ if (!register_data_exchange_spec(config, sdp_record))
+ goto error;
+
+ register_mcap_features(sdp_record);
+
+ adapter_get_address(hdps->adapter->btd_adapter, &addr);
+
+ if (add_record_to_server(&addr, sdp_record) < 0)
+ goto error;
+ hdps->sdp_handler = sdp_record->handle;
+ return TRUE;
+error:
+ if (sdp_record)
+ sdp_record_free(sdp_record);
+ return FALSE;
+}
diff --git a/health/hdp_util.h b/health/hdp_util.h
index f09e9a6..fb114c7 100644
--- a/health/hdp_util.h
+++ b/health/hdp_util.h
@@ -30,5 +30,6 @@
#include "hdp_types.h"

struct hdp_config *hdp_get_config(DBusMessageIter *iter, GError **err);
+gboolean hdp_register_sdp_record(struct hdp_instance *hdps);

#endif /* __HDP_UTIL_H__ */
--
1.6.3.3


Subject: [PATCH 05/32] Register healt_driver interfaces in d-bus

From: José Antonio Santos Cadenas <[email protected]>

Register interface for each hdp_driver plugged
Unregister driver
Device_get_health_instances method added
Add function to get data exchange spec from a remote record
---
health/hdp.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++---
health/hdp.h | 4 +-
health/hdp_types.h | 10 ++++
health/hdp_util.c | 11 +++++
health/hdp_util.h | 2 +
health/manager.c | 20 +++-----
6 files changed, 151 insertions(+), 22 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index dbe326e..5b09ab3 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -29,12 +29,16 @@

#include "hdp_types.h"
#include "hdp_util.h"
-
-#define HEALTH_MANAGER_INTERFACE "org.bluez.HealthAdapter"
+#include "device.h"

#include "../src/dbus-common.h"

+#define HEALTH_MANAGER_INTERFACE "org.bluez.HealthAdapter"
+#define HEALTH_INSTANCE_INTERFACE "org.bluez.HealthInstance"
+#define HEALTH_DEVICE "org.bluez.HealthDevice"
+
static GSList *adapters = NULL;
+static GSList *devices = NULL;

static struct hdp_adapter *find_adapter(GSList *list,
struct btd_adapter *btd_adapter)
@@ -51,6 +55,98 @@ static struct hdp_adapter *find_adapter(GSList *list,
return NULL;
}

+static struct hdp_device *find_device(GSList *devices, struct btd_device *dev)
+{
+ GSList *l;
+ struct hdp_device *device;
+
+ for (l = devices; l != NULL; l = l->next) {
+ device = l->data;
+
+ if (device->dev == dev)
+ return device;
+ }
+
+ return NULL;
+}
+
+static DBusMessage *get_health_instances(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct hdp_device *device = user_data;
+ const sdp_record_t *rec;
+ guint8 data_spec;
+
+ rec = btd_device_get_record(device->dev, HDP_UUID);
+
+ if (!rec) {
+ DBG("No record found");
+ goto error;
+ }
+
+ if (!hdp_get_data_exchange_spec(rec, &data_spec))
+ goto error;
+
+ DBG("Get data exchange spec %d", data_spec);
+error:
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "Cannot get the remote SDP record");
+}
+
+static void health_device_free(struct hdp_device *device)
+{
+ if (device->conn) {
+ dbus_connection_unref(device->conn);
+ device->conn = NULL;
+ }
+
+ if (device->dev) {
+ btd_device_unref(device->dev);
+ device->dev = NULL;
+ }
+
+ g_free(device);
+}
+
+static void dev_path_unregister(void *data)
+{
+ struct hdp_device *device = data;
+
+
+ DBG("Unregistered interface %s on path %s", HEALTH_DEVICE,
+ device_get_path(device->dev));
+ devices = g_slist_remove(devices, device);
+ health_device_free(device);
+}
+
+static GDBusMethodTable device_methods[] = {
+ { "GetHealthInstances", "", "a{sv}", get_health_instances },
+ { NULL }
+};
+
+static struct hdp_device *create_health_device(DBusConnection *conn,
+ struct btd_device *device)
+{
+ const gchar *path = device_get_path(device);
+ struct hdp_device *dev;
+
+ dev = g_new0(struct hdp_device, 1);
+ dev->conn = dbus_connection_ref(conn);
+ dev->dev = btd_device_ref(device);
+
+ if (!g_dbus_register_interface(conn, path,
+ HEALTH_DEVICE,
+ device_methods, NULL, NULL,
+ dev, dev_path_unregister)) {
+ error("D-Bus failed to register %s interface", HEALTH_DEVICE);
+ health_device_free(dev);
+ return NULL;
+ }
+
+ DBG("Registered interface %s on path %s", HEALTH_DEVICE, path);
+ return dev;
+}
+
static void hdp_set_instance_id(struct hdp_instance *hdpi)
{
struct hdp_adapter *adapter = hdpi->adapter;
@@ -59,7 +155,7 @@ static void hdp_set_instance_id(struct hdp_instance *hdpi)
}

static DBusMessage *hdp_create_instance(DBusConnection *conn,
- DBusMessage *msg, void *user_data)
+ DBusMessage *msg, void *user_data)
{
struct hdp_adapter *adapter = user_data;
const char *path, *name;
@@ -185,13 +281,29 @@ void hdp_adapter_unregister(struct btd_adapter *btd_adapter)
DBG("HDP exit");
}

-int health_device_register(struct btd_device *device, const char *uuid)
+int hdp_device_register(DBusConnection *conn, struct btd_device *device)
{
- DBG("HDP_DRIVER_PROBE with uuid %s", uuid);
+ struct hdp_device *hdp_dev;
+
+ hdp_dev = find_device(devices, device);
+ if (!hdp_dev) {
+ hdp_dev = create_health_device(conn, device);
+ if (!hdp_dev)
+ return -1;
+ devices = g_slist_append(devices, hdp_dev);
+ }
return 0;
}

-void health_device_unregister(struct btd_device *device)
+void hdp_device_unregister(struct btd_device *device)
{
- DBG("TODO: Remove device");
+ struct hdp_device *hdp_dev;
+ const char *path;
+
+ hdp_dev = find_device(devices, device);
+ if (!hdp_dev)
+ return;
+
+ path = device_get_path(hdp_dev->dev);
+ g_dbus_unregister_interface(hdp_dev->conn, path, HEALTH_DEVICE);
}
diff --git a/health/hdp.h b/health/hdp.h
index 0aae7b9..edb06a0 100644
--- a/health/hdp.h
+++ b/health/hdp.h
@@ -26,5 +26,5 @@
int hdp_adapter_register(DBusConnection *conn, struct btd_adapter *btd_adapter);
void hdp_adapter_unregister(struct btd_adapter *btd_adapter);

-int health_device_register(struct btd_device *device, const char *uuid);
-void health_device_unregister(struct btd_device *device);
+int hdp_device_register(DBusConnection *conn, struct btd_device *device);
+void hdp_device_unregister(struct btd_device *device);
diff --git a/health/hdp_types.h b/health/hdp_types.h
index 2db9adf..171910a 100644
--- a/health/hdp_types.h
+++ b/health/hdp_types.h
@@ -30,6 +30,10 @@
#include <glib.h>
#include "mcap_lib.h"

+#define HDP_UUID "00001400-0000-1000-8000-00805F9B34FB"
+#define HDP_SOURCE_UUID "00001401-0000-1000-8000-00805F9B34FB"
+#define HDP_SINK_UUID "00001402-0000-1000-8000-00805F9B34FB"
+
#define HDP_SERVICE_NAME "Bluez HDP"
#define HDP_SERVICE_DSC "A Bluez health device profile implementation"
#define HDP_SERVICE_PROVIDER "Bluez"
@@ -96,4 +100,10 @@ struct hdp_instance {
uint32_t sdp_handler; /* SDP record handler */
};

+struct hdp_device {
+ DBusConnection *conn; /* for name listener handling */
+ struct btd_device *dev; /* Device reference */
+ struct hdp_adapter *hdp_adapter; /* hdp_adapater */
+};
+
#endif /* __HDP_TYPES_H__ */
diff --git a/health/hdp_util.c b/health/hdp_util.c
index cc5dc2e..96fa737 100644
--- a/health/hdp_util.c
+++ b/health/hdp_util.c
@@ -838,3 +838,14 @@ error:
sdp_record_free(sdp_record);
return FALSE;
}
+
+gboolean hdp_get_data_exchange_spec(const sdp_record_t *rec, guint8 *val)
+{
+ sdp_data_t *exspec;
+
+ exspec = sdp_data_get(rec, SDP_ATTR_DATA_EXCHANGE_SPEC);
+ if (exspec->dtd != SDP_UINT8)
+ return FALSE;
+ *val = exspec->val.uint8;
+ return TRUE;
+}
diff --git a/health/hdp_util.h b/health/hdp_util.h
index fb114c7..0fdaaec 100644
--- a/health/hdp_util.h
+++ b/health/hdp_util.h
@@ -31,5 +31,7 @@

struct hdp_config *hdp_get_config(DBusMessageIter *iter, GError **err);
gboolean hdp_register_sdp_record(struct hdp_instance *hdps);
+gboolean hdp_get_data_exchange_spec(const sdp_record_t *rec, guint8 *val);
+void hdp_instance_free(struct hdp_instance *hdpi);

#endif /* __HDP_UTIL_H__ */
diff --git a/health/manager.c b/health/manager.c
index d163545..8b52e93 100644
--- a/health/manager.c
+++ b/health/manager.c
@@ -28,20 +28,19 @@
#endif

#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>

#include <glib.h>
#include <gdbus.h>

-#include "adapter.h"
-#include "device.h"
+#include "hdp_types.h"

#include "log.h"
#include "manager.h"
#include "hdp.h"

-#define HDP_UUID "00001400-0000-1000-8000-00805F9B34FB"
-#define HDP_SOURCE_UUID "00001401-0000-1000-8000-00805F9B34FB"
-#define HDP_SINK_UUID "00001402-0000-1000-8000-00805F9B34FB"
+#include "device.h"
+#include "glib-helper.h"

static DBusConnection *connection = NULL;

@@ -63,22 +62,17 @@ static struct btd_adapter_driver hdp_adapter_driver = {

static int hdp_driver_probe(struct btd_device *device, GSList *uuids)
{
- while (uuids) {
- health_device_register(device, uuids->data);
- uuids = uuids->next;
- }
-
- return 0;
+ return hdp_device_register(connection, device);
}

static void hdp_driver_remove(struct btd_device *device)
{
- health_device_unregister(device);
+ hdp_device_unregister(device);
}

static struct btd_device_driver hdp_device_driver = {
.name = "hdp-device-driver",
- .uuids = BTD_UUIDS(HDP_UUID, HDP_SOURCE_UUID, HDP_SINK_UUID),
+ .uuids = BTD_UUIDS(HDP_UUID),
.probe = hdp_driver_probe,
.remove = hdp_driver_remove,
};
--
1.6.3.3


Subject: [PATCH 16/32] Register Health link int the bus when MCL is connected

---
health/hdp.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++-----
health/hdp_types.h | 12 +++++++++-
2 files changed, 68 insertions(+), 8 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index f5dc107..752714a 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -37,8 +37,9 @@

#include "../src/dbus-common.h"

-#define HEALTH_MANAGER_INTERFACE "org.bluez.HealthAdapter"
-#define HEALTH_DEVICE "org.bluez.HealthDevice"
+#define HEALTH_MANAGER_INTERFACE "org.bluez.HealthAdapter"
+#define HEALTH_DEVICE "org.bluez.HealthDevice"
+#define HEALTH_LINK "org.bluez.HealthLink"

static GSList *adapters = NULL;
static GSList *devices = NULL;
@@ -101,6 +102,18 @@ static int hdp_instance_idcmp(gconstpointer instance, gconstpointer p)
return -1;
}

+static void set_health_link_path(struct hdp_link *hdpl)
+{
+ char path[MAX_PATH_LENGTH + 1];
+
+ hdpl->id = hdpl->hdpi->hlc++;
+ snprintf(path, MAX_PATH_LENGTH, "%s/health_link_%d_%d",
+ adapter_get_path(hdpl->hdpi->adapter->btd_adapter),
+ hdpl->hdpi->id, hdpl->id);
+
+ hdpl->path = g_strdup(path);
+}
+
static void fill_up_one_spec(DBusMessageIter *dict, gpointer data)
{
struct hdp_feature *feature = data;
@@ -346,12 +359,23 @@ static uint8_t hdp_mcap_mdl_reconn_req_cb(struct mcap_mcl *mcl,
return MCAP_REQUEST_NOT_SUPPORTED;
}

+static void health_link_path_unregister(void *data)
+{
+ /* struct hdp_link *hdpl = data */
+ /* TODO: Unregister hdp_link*/
+}
+
+static GDBusMethodTable health_link_methods[] = {
+ { NULL }
+};
+
static void hdp_mcl_connect_cb(struct mcap_mcl *mcl, GError *err, void *data)
{
struct hdp_connection_cb *cb_data = data;
struct hdp_device *device = cb_data->device;
- /* struct hdp_instance *hdpi = cb_data->hdpi; */
+ struct hdp_instance *hdpi = cb_data->hdpi;
DBusMessage *msg = cb_data->msg;
+ struct hdp_link *hdpl = NULL;
GError *cberr = NULL;
DBusMessage *reply;

@@ -360,8 +384,13 @@ static void hdp_mcl_connect_cb(struct mcap_mcl *mcl, GError *err, void *data)
if (err)
goto fail;

- /* Create and Register HealthLink interface */
- mcap_mcl_set_cb(mcl, &cberr, NULL /*health_link*/,
+ hdpl = g_new0(struct hdp_link, 1);
+ hdpl->hdpi = hdpi;
+ hdpl->dev = device;
+ hdpl->mcl = mcap_mcl_ref(mcl);
+ set_health_link_path(hdpl);
+
+ mcap_mcl_set_cb(mcl, &cberr, hdpl,
MCAP_MDL_CB_CONNECTED, hdp_mcap_mdl_connected_cb,
MCAP_MDL_CB_CLOSED, hdp_mcap_mdl_closed_cb,
MCAP_MDL_CB_DELETED, hdp_mcap_mdl_deleted_cb,
@@ -369,14 +398,35 @@ static void hdp_mcl_connect_cb(struct mcap_mcl *mcl, GError *err, void *data)
MCAP_MDL_CB_REMOTE_CONN_REQ, hdp_mcap_mdl_conn_req_cb,
MCAP_MDL_CB_REMOTE_RECONN_REQ, hdp_mcap_mdl_reconn_req_cb,
MCAP_MDL_CB_INVALID);
+
if (cberr)
goto fail;
+
+ if (!g_dbus_register_interface(hdpl->dev->conn, hdpl->path, HEALTH_LINK,
+ health_link_methods, NULL, NULL,
+ hdpl, health_link_path_unregister)) {
+ error("D-Bus failed to register %s interface to %s",
+ HEALTH_LINK, hdpl->path);
+ goto fail;
+ }
+
+ hdpi->hlink = g_slist_prepend(hdpi->hlink, hdpl);
+ reply = g_dbus_create_reply(msg, DBUS_TYPE_OBJECT_PATH, &hdpl->path,
+ DBUS_TYPE_INVALID);
+ g_dbus_send_message(device->conn, reply);
return;
fail:
reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HdpError",
(err ? err->message : cberr->message));
- if (!cberr)
+ if (cberr) {
+ /* MCAP will close the MCL and won't cache it if we didn't
+ * increase the MCL reference counter during the callback. */
+ mcap_mcl_unref(hdpl->mcl);
+ g_free(hdpl->path);
+ g_free(hdpl);
g_error_free(cberr);
+ }
+
g_dbus_send_message(device->conn, reply);
}

@@ -421,8 +471,8 @@ static void connect_health_instance(sdp_list_t *recs, int err, gpointer data)
reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
"Error getting remote protocol descriptor list");
fail:
- g_dbus_send_message(device->conn, reply);
g_free(cb_data);
+ g_dbus_send_message(device->conn, reply);
}

static DBusMessage *hdp_connect(DBusConnection *conn,
diff --git a/health/hdp_types.h b/health/hdp_types.h
index 3bab4ea..05bfbfe 100644
--- a/health/hdp_types.h
+++ b/health/hdp_types.h
@@ -99,10 +99,20 @@ struct hdp_instance {
struct hdp_config *config; /* Configuration */
uint32_t sdp_handler; /* SDP record handler */
guint dbus_watcher; /* Client D-Bus conn watcher */
+ uint16_t hlc; /* Health link id. counter */
+};
+
+struct hdp_link {
+ struct hdp_instance *hdpi; /* HDP session */
+ struct hdp_device *dev; /* Health Device */
+ struct mcap_mcl *mcl; /* MCAP mcl */
+ GSList *channels; /* Data channels */
+ char *path; /* HDP link path */
+ uint32_t id; /* Health link id */
};

struct hdp_device {
- DBusConnection *conn; /* for name listener handling */
+ DBusConnection *conn; /* For name listener handling */
struct btd_device *dev; /* Device reference */
struct hdp_adapter *hdp_adapter; /* hdp_adapater */
};
--
1.6.3.3


Subject: [PATCH 08/32] Work in getting remote SDP records

---
health/hdp.c | 80 ++++++++++++++++++++++++++++++++++++++++++++--------
health/hdp_util.c | 5 +++
health/hdp_util.h | 1 +
3 files changed, 73 insertions(+), 13 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 31abb4c..4fb9d62 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -31,6 +31,8 @@
#include "hdp_util.h"
#include "device.h"

+#include "glib-helper.h"
+
#include "../src/dbus-common.h"

#define HEALTH_MANAGER_INTERFACE "org.bluez.HealthAdapter"
@@ -39,6 +41,11 @@
static GSList *adapters = NULL;
static GSList *devices = NULL;

+struct instances_aux {
+ struct hdp_device *device;
+ DBusMessage *msg;
+};
+
static struct hdp_adapter *find_adapter(GSList *list,
struct btd_adapter *btd_adapter)
{
@@ -69,27 +76,73 @@ static struct hdp_device *find_device(GSList *devices, struct btd_device *dev)
return NULL;
}

+static void sink_health_instances(sdp_list_t *recs, int err, gpointer user_data)
+{
+ struct instances_aux *cb_data = user_data;
+ DBusMessage *msg = cb_data->msg;
+ struct hdp_device *device = cb_data->device;
+ DBusMessage *reply;
+ sdp_record_t *rec;
+ sdp_list_t *l;
+ guint8 data_spec;
+ GSList *end_points;
+
+ g_free(cb_data);
+
+ if (err != 0) {
+ error("Error getting sink records");
+ reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "Error getting remote information");
+ g_dbus_send_message(device->conn, reply);
+ return;
+ }
+
+ for (l = recs; l; l = l->next) {
+ rec = l->data;
+ DBG("Record found 0x%x", rec->handle);
+ /* TODO: Check record */
+ if (!hdp_get_data_exchange_spec(rec, &data_spec)) {
+ error("Error getting data exchange info");
+ continue;
+ }
+ end_points = hdp_get_end_points(rec);
+ if (!end_points) {
+ error("Error getting end points");
+ continue;
+ }
+ DBG("Get data exchange spec %d", data_spec);
+ }
+
+ reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "Not implemented yet");
+ g_dbus_send_message(device->conn, reply);
+}
+
static DBusMessage *get_health_instances(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
struct hdp_device *device = user_data;
- const sdp_record_t *rec;
- guint8 data_spec;
+ struct btd_adapter *adapter;
+ struct instances_aux *cb_data;
+ bdaddr_t src, dst;
+ uuid_t uuid;

- rec = btd_device_get_record(device->dev, HDP_UUID);
+ adapter = device_get_adapter(device->dev);
+ adapter_get_address(adapter, &src);
+ device_get_address(device->dev, &dst);

- if (!rec) {
- DBG("No record found");
- goto error;
- }
+ cb_data = g_new0(struct instances_aux, 1);
+ cb_data->device = device;
+ cb_data->msg = dbus_message_ref(msg);

- if (!hdp_get_data_exchange_spec(rec, &data_spec))
- goto error;

- DBG("Get data exchange spec %d", data_spec);
-error:
+ bt_string2uuid(&uuid, HDP_UUID);
+ if (bt_search_service(&src, &dst, &uuid, sink_health_instances,
+ cb_data, NULL) == 0)
+ return NULL;
+
return g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
- "Cannot get the remote SDP record");
+ "Error getting remote information");
}

static void health_device_free(struct hdp_device *device)
@@ -119,7 +172,8 @@ static void dev_path_unregister(void *data)
}

static GDBusMethodTable device_methods[] = {
- { "GetHealthInstances", "", "a{sv}", get_health_instances },
+ { "GetHealthInstances", "", "a{sv}", get_health_instances,
+ G_DBUS_METHOD_FLAG_ASYNC },
{ NULL }
};

diff --git a/health/hdp_util.c b/health/hdp_util.c
index 5397f11..731154b 100644
--- a/health/hdp_util.c
+++ b/health/hdp_util.c
@@ -781,6 +781,11 @@ static gboolean register_data_exchange_spec(struct hdp_config *config,
return TRUE;
}

+GSList *hdp_get_end_points(const sdp_record_t *rec)
+{
+ return NULL;
+}
+
static gboolean register_mcap_features(sdp_record_t *sdp_record)
{
sdp_data_t *mcap_proc;
diff --git a/health/hdp_util.h b/health/hdp_util.h
index 0fdaaec..beafa00 100644
--- a/health/hdp_util.h
+++ b/health/hdp_util.h
@@ -32,6 +32,7 @@
struct hdp_config *hdp_get_config(DBusMessageIter *iter, GError **err);
gboolean hdp_register_sdp_record(struct hdp_instance *hdps);
gboolean hdp_get_data_exchange_spec(const sdp_record_t *rec, guint8 *val);
+GSList *hdp_get_end_points(const sdp_record_t *rec);
void hdp_instance_free(struct hdp_instance *hdpi);

#endif /* __HDP_UTIL_H__ */
--
1.6.3.3


Subject: [PATCH 11/32] Initial support for connecting instances

---
health/hdp.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 50 insertions(+), 12 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 8690f0e..8bb6057 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -76,6 +76,16 @@ static struct hdp_device *find_device(GSList *devices, struct btd_device *dev)
return NULL;
}

+static int hdp_instance_idcmp(gconstpointer instance, gconstpointer p)
+{
+ const struct hdp_instance *hdpi = instance;
+ const uint32_t *id = p;
+
+ if (hdpi->id == *id)
+ return 0;
+ return -1;
+}
+
static void append_dict_features(DBusMessageIter *iter, GSList *end_points)
{
DBusMessageIter entry, array;
@@ -225,9 +235,40 @@ static void dev_path_unregister(void *data)
health_device_free(device);
}

+static DBusMessage *hdp_connect(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct hdp_device *device = user_data;
+ struct hdp_instance *hdpi;
+ uint32_t lid, rid;
+ GSList *l;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &lid,
+ DBUS_TYPE_UINT32, &rid,
+ DBUS_TYPE_INVALID)) {
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ }
+
+ l = g_slist_find_custom(device->hdp_adapter->instances, &lid,
+ hdp_instance_idcmp);
+ if (!l)
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid local instance id");
+ hdpi = l->data;
+
+
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".HdpError",
+ "Function is not yet implemented");
+}
+
static GDBusMethodTable device_methods[] = {
{ "GetHealthInstances", "", "aa{sv}", get_health_instances,
G_DBUS_METHOD_FLAG_ASYNC },
+ { "Connect", "uu", "o", hdp_connect, G_DBUS_METHOD_FLAG_ASYNC },
{ NULL }
};

@@ -235,33 +276,30 @@ static struct hdp_device *create_health_device(DBusConnection *conn,
struct btd_device *device)
{
const gchar *path = device_get_path(device);
+ struct btd_adapter *adapter = device_get_adapter(device);
struct hdp_device *dev;

dev = g_new0(struct hdp_device, 1);
dev->conn = dbus_connection_ref(conn);
dev->dev = btd_device_ref(device);
+ dev->hdp_adapter = find_adapter(adapters, adapter);
+
+ if (!dev->hdp_adapter)
+ goto fail;

if (!g_dbus_register_interface(conn, path,
HEALTH_DEVICE,
device_methods, NULL, NULL,
dev, dev_path_unregister)) {
error("D-Bus failed to register %s interface", HEALTH_DEVICE);
- health_device_free(dev);
- return NULL;
+ goto fail;
}

DBG("Registered interface %s on path %s", HEALTH_DEVICE, path);
return dev;
-}
-
-static int hdp_instance_idcmp(gconstpointer instance, gconstpointer p)
-{
- const struct hdp_instance *hdpi = instance;
- const uint32_t *id = p;
-
- if (hdpi->id == *id)
- return 0;
- return -1;
+fail:
+ health_device_free(dev);
+ return NULL;
}

static void hdp_set_instance_id(struct hdp_instance *hdpi)
--
1.6.3.3


Subject: [PATCH 17/32] Analize remote record looking for psm

---
health/hdp.c | 1 +
health/hdp_util.c | 52 +++++++++++++++++++++++++++++++++++++---------------
2 files changed, 38 insertions(+), 15 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 752714a..d38aff1 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -462,6 +462,7 @@ static void connect_health_instance(sdp_list_t *recs, int err, gpointer data)
goto fail;
}

+ info("psm = 0x%x, version = 0x%x", ccpsm, version);
device_get_address(device->dev, &dst);
mcap_create_mcl(hdpi->mi, &dst, ccpsm, &gerr, hdp_mcl_connect_cb,
cb_data);
diff --git a/health/hdp_util.c b/health/hdp_util.c
index a7fc1d5..2706a74 100644
--- a/health/hdp_util.c
+++ b/health/hdp_util.c
@@ -884,29 +884,51 @@ gboolean hdp_get_data_exchange_spec(const sdp_record_t *rec, guint8 *val)
return TRUE;
}

+static gboolean get_prot_desc_entry(sdp_data_t *entry, int type, guint16 *val)
+{
+ sdp_data_t *iter;
+ int proto;
+
+ if (!entry || (entry->dtd != SDP_SEQ8))
+ return FALSE;
+
+ iter = entry->val.dataseq;
+ if (!(iter->dtd & SDP_UUID_UNSPEC))
+ return FALSE;
+
+ proto = sdp_uuid_to_proto(&iter->val.uuid);
+ if (proto != type)
+ return FALSE;
+
+ iter = iter->next;
+ if (iter->dtd != SDP_UINT16)
+ return FALSE;
+ if (val) {
+ *val = iter->val.uint16;
+ }
+ return TRUE;
+}
+
gboolean hdp_get_prot_desc_list(const sdp_record_t *rec, guint16 *psm,
guint16 *version)
{
- if (!(psm || version))
+ sdp_data_t *pdl, *p0, *p1;
+
+ if (!psm && !version)
return TRUE;

- /* TODO:
- sdp_data_t *pdl, *l;
+ pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
+ if (pdl->dtd != SDP_SEQ8)
+ return FALSE;
+
+ p0 = pdl->val.dataseq;

- exspec = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
- if (exspec->dtd != SDP_SEQ8)
+ if (!get_prot_desc_entry(p0, L2CAP_UUID, psm))
+ return FALSE;
+ p1 = p0->next;
+ if (!get_prot_desc_entry(p1, MCAP_CTRL_UUID, version))
return FALSE;

- for (l = pdl->val.dataseq; l; l = l->next) {
- if (l->dtd != SDP_SEQ8)
- continue;
- epl = get_feature(epl, l->val.dataseq);
- }
- */
- if (psm)
- *psm = 0x1001;
- if (version)
- *version = 0x0100;
return TRUE;
}

--
1.6.3.3


Subject: [PATCH 21/32] Manage mcap disconnections and reconnections

---
health/hdp.c | 58 ++++++++++++++++++++++++++++++++++++++++++---------
health/hdp_types.h | 5 ++++
health/hdp_util.c | 19 ++++++++++++----
3 files changed, 66 insertions(+), 16 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 4ed0150..310f6ff 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -37,10 +37,6 @@

#include "../src/dbus-common.h"

-#define HEALTH_MANAGER_INTERFACE "org.bluez.HealthAdapter"
-#define HEALTH_DEVICE "org.bluez.HealthDevice"
-#define HEALTH_LINK "org.bluez.HealthLink"
-
static GSList *adapters = NULL;
static GSList *devices = NULL;

@@ -102,6 +98,14 @@ static int hdp_instance_idcmp(gconstpointer instance, gconstpointer p)
return -1;
}

+static int hdp_link_mcl_cmp(gconstpointer link, gconstpointer mcl){
+ const struct hdp_link *hdpl = link;
+
+ if (hdpl->mcl == mcl)
+ return 0;
+ return -1;
+}
+
static void set_health_link_path(struct hdp_link *hdpl)
{
char path[MAX_PATH_LENGTH + 1];
@@ -617,7 +621,7 @@ static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
struct hdp_link *hdpl;
GError *err = NULL;

- DBG("TODO: implement mcl_connected");
+ DBG("mcl_connected");
hdpl = create_health_link(hdpi, mcl, &err);
if (err)
return;
@@ -627,20 +631,52 @@ static void mcl_connected(struct mcap_mcl *mcl, gpointer data)

static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
{
- /* struct hdp_instance *hdpi = data; */
- DBG("TODO: implement mcl_reconnected");
+ struct hdp_instance *hdpi = data;
+ struct hdp_link *hdpl;
+ GSList *l;
+
+ DBG("mcl_reconnected");
+ l = g_slist_find_custom(hdpi->hlink, mcl, hdp_link_mcl_cmp);
+ if (l) {
+ hdpl = l->data;
+ hdpl->closed = FALSE;
+ return;
+ }
+
+ mcl_connected(mcl, data);
}

static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
{
- /* struct hdp_instance *hdpi = data; */
- DBG("TODO: implement mcl_disconnected");
+ struct hdp_instance *hdpi = data;
+ struct hdp_link *hdpl;
+ GSList *l;
+
+ DBG("mcl_disconnected");
+ l = g_slist_find_custom(hdpi->hlink, mcl, hdp_link_mcl_cmp);
+ if (!l)
+ return;
+
+ hdpl = l->data;
+ hdpl->closed = TRUE;
}

static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
{
- /* struct hdp_instance *hdpi = data; */
- DBG("TODO: implement mcl_uncached");
+ struct hdp_instance *hdpi = data;
+ struct hdp_link *hdpl;
+ GSList *l;
+
+ DBG("mcl_uncached");
+ l = g_slist_find_custom(hdpi->hlink, mcl, hdp_link_mcl_cmp);
+ if (!l)
+ return;
+
+ hdpl = l->data;
+
+ hdpi->hlink = g_slist_remove(hdpi->hlink, hdpl);
+ g_dbus_unregister_interface(hdpi->adapter->conn, hdpl->path,
+ HEALTH_LINK);
}

static DBusMessage *hdp_create_instance(DBusConnection *conn,
diff --git a/health/hdp_types.h b/health/hdp_types.h
index 79419a1..5db8e0d 100644
--- a/health/hdp_types.h
+++ b/health/hdp_types.h
@@ -42,6 +42,10 @@

#define HDP_ERROR g_quark_from_static_string("hdp-error-quark")

+#define HEALTH_MANAGER_INTERFACE "org.bluez.HealthAdapter"
+#define HEALTH_DEVICE "org.bluez.HealthDevice"
+#define HEALTH_LINK "org.bluez.HealthLink"
+
typedef enum {
HDP_DIC_PARSE_ERROR,
HDP_DIC_ENTRY_PARSE_ERROR,
@@ -105,6 +109,7 @@ struct hdp_instance {
struct hdp_link {
struct hdp_instance *hdpi; /* HDP session */
struct mcap_mcl *mcl; /* MCAP mcl */
+ gboolean closed; /* MCL is closed but cached */
GSList *channels; /* Data channels */
char *path; /* HDP link path */
uint32_t id; /* Health link id */
diff --git a/health/hdp_util.c b/health/hdp_util.c
index 2706a74..d09ee6d 100644
--- a/health/hdp_util.c
+++ b/health/hdp_util.c
@@ -109,15 +109,24 @@ static void free_config(struct hdp_config *config)
g_free(config);
}

+static void hdp_link_unregister(gpointer link, gpointer data)
+{
+ struct hdp_link *hdpl = link;
+ struct hdp_instance *hdpi = hdpl->hdpi;
+
+ hdpi->hlink = g_slist_remove(hdpi->hlink, hdpl);
+ g_dbus_unregister_interface(hdpi->adapter->conn, hdpl->path,
+ HEALTH_LINK);
+}
+
void hdp_instance_free(struct hdp_instance *hdpi)
{
DBG("HDP instance %d is deleted", hdpi->id);
/* TODO: Complete this part */
- /*
- g_slist_foreach(hdpi->devices, hdp_device_unregister, NULL);
- g_slist_free(hdpi->devices);
- hdpi->devices = NULL;
- */
+
+ g_slist_foreach(hdpi->hlink, hdp_link_unregister, NULL);
+ g_slist_free(hdpi->hlink);
+ hdpi->hlink = NULL;

if (hdpi->dbus_watcher)
g_dbus_remove_watch(hdpi->adapter->conn, hdpi->dbus_watcher);
--
1.6.3.3


Subject: [PATCH 02/32] Initial support for HDP

Creates the basic structure of the plugin, registering
adapter and device drivers.
---
Makefile.am | 8 +
acinclude.m4 | 8 +-
health/hdp.c | 183 +++++++++++++++++++++
health/hdp.h | 27 +++
health/hdp_types.h | 99 ++++++++++++
health/hdp_util.c | 447 ++++++++++++++++++++++++++++++++++++++++++++++++++++
health/hdp_util.h | 34 ++++
health/main.c | 60 +++++++
health/manager.c | 101 ++++++++++++
health/manager.h | 27 +++
10 files changed, 993 insertions(+), 1 deletions(-)
create mode 100644 health/hdp.c
create mode 100644 health/hdp.h
create mode 100644 health/hdp_types.h
create mode 100644 health/hdp_util.c
create mode 100644 health/hdp_util.h
create mode 100644 health/main.c
create mode 100644 health/manager.c
create mode 100644 health/manager.h

diff --git a/Makefile.am b/Makefile.am
index f96d556..9be4865 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -169,6 +169,14 @@ builtin_modules += service
builtin_sources += plugins/service.c
endif

+if HEALTHPLUGIN
+builtin_modules += health
+builtin_sources += health/main.c \
+ health/manager.h health/manager.c \
+ health/hdp.h health/hdp.c \
+ health/hdp_util.h health/hdp_util.c
+endif
+
if MCAP
mcap_sources += mcap/mcap_internal.h \
mcap/mcap_lib.h mcap/sync.c \
diff --git a/acinclude.m4 b/acinclude.m4
index b512cfb..23c594a 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -167,6 +167,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
serial_enable=yes
network_enable=yes
service_enable=yes
+ health_enable=no
mcap_enable=no
pnat_enable=no
tracer_enable=no
@@ -216,6 +217,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
service_enable=${enableval}
])

+ AC_ARG_ENABLE(health, AC_HELP_STRING([--enable-health], [enable health plugin]), [
+ health_enable=${enableval}
+ ])
+
AC_ARG_ENABLE(mcap, AC_HELP_STRING([--enable-mcap], [enable mcap support]), [
mcap_enable=${enableval}
])
@@ -330,7 +335,8 @@ AC_DEFUN([AC_ARG_BLUEZ], [
AM_CONDITIONAL(SERIALPLUGIN, test "${serial_enable}" = "yes")
AM_CONDITIONAL(NETWORKPLUGIN, test "${network_enable}" = "yes")
AM_CONDITIONAL(SERVICEPLUGIN, test "${service_enable}" = "yes")
- AM_CONDITIONAL(MCAP, test "${mcap_enable}" = "yes")
+ AM_CONDITIONAL(HEALTHPLUGIN, test "${health_enable}" = "yes")
+ AM_CONDITIONAL(MCAP, test "${mcap_enable}" = "yes" || test "${health_enable}" = "yes")
AM_CONDITIONAL(ECHOPLUGIN, test "no" = "yes")
AM_CONDITIONAL(PNATPLUGIN, test "${pnat_enable}" = "yes")
AM_CONDITIONAL(TRACER, test "${tracer_enable}" = "yes")
diff --git a/health/hdp.c b/health/hdp.c
new file mode 100644
index 0000000..2dec069
--- /dev/null
+++ b/health/hdp.c
@@ -0,0 +1,183 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <gdbus.h>
+
+#include "log.h"
+#include "error.h"
+
+#include "hdp_types.h"
+#include "hdp_util.h"
+
+#define HEALTH_MANAGER_INTERFACE "org.bluez.HealthAdapter"
+
+#include "../src/dbus-common.h"
+
+static GSList *adapters = NULL;
+
+static struct hdp_adapter *find_adapter(GSList *list,
+ struct btd_adapter *btd_adapter)
+{
+ GSList *l;
+ struct hdp_adapter *adapter;
+
+ for (l = list; l; l = l->next) {
+ adapter = l->data;
+ if (adapter->btd_adapter == btd_adapter)
+ return adapter;
+ }
+
+ return NULL;
+}
+
+static void hdp_set_instance_id(struct hdp_instance *hdpi)
+{
+ struct hdp_adapter *adapter = hdpi->adapter;
+
+ hdpi->id = adapter->ic++;
+}
+
+static DBusMessage *hdp_create_instance(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct hdp_adapter *adapter = user_data;
+ const char *path, *name;
+ DBusMessageIter iter;
+ GError *err = NULL;
+ DBusMessage *reply;
+ struct hdp_instance *hdpi;
+ struct hdp_config *config;
+ int ctype;
+
+ dbus_message_iter_init(msg, &iter);
+ ctype = dbus_message_iter_get_arg_type(&iter);
+ if (ctype != DBUS_TYPE_OBJECT_PATH)
+ goto error;
+ dbus_message_iter_get_basic(&iter, &path);
+ dbus_message_iter_next(&iter);
+ config = hdp_get_config(&iter, &err);
+ if (err)
+ goto error;
+ name = dbus_message_get_sender(msg);
+ if (!name) {
+ g_set_error(&err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+ "Can't get sender name");
+ goto error;
+ }
+
+ hdpi = g_new0(struct hdp_instance, 1);
+ hdpi->adapter = adapter;
+ hdpi->aname = g_strdup(name);
+ hdpi->apath = g_strdup(path);
+ hdpi->config = config;
+ if (!config->svc_dsc)
+ config->svc_dsc = g_strdup(HDP_SERVICE_DSC);
+ if (!config->svc_name)
+ config->svc_name = g_strdup(HDP_SERVICE_NAME);
+ if (!config->svc_prov)
+ config->svc_prov = g_strdup(HDP_SERVICE_PROVIDER);
+ hdp_set_instance_id(hdpi);
+
+ /* TODO: Create mcap instance */
+
+ /* TODO: Create SDP record if needed. */
+
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".HealthError",
+ "Incomplete call yet");
+error:
+ if (err) {
+ reply = g_dbus_create_error(msg,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments: %s", err->message);
+ g_error_free(err);
+ } else
+ reply = g_dbus_create_error(msg,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return reply;
+}
+
+static GDBusMethodTable hdp_methods[] = {
+ { "CreateInstance", "oa{sv}", "u", hdp_create_instance },
+ { NULL }
+};
+
+void hdp_delete_instance_iter(gpointer data, gpointer user_data)
+{
+ /* struct hdp_instance *hdpi = data; */
+
+ /* TODO: Create a free function */
+}
+
+static void hdp_path_unregister(void *data)
+{
+ struct hdp_adapter *adapter = data;
+
+ g_slist_foreach(adapter->instances, hdp_delete_instance_iter, NULL);
+ g_slist_free(adapter->instances);
+ adapter->instances = NULL;
+ DBG("All hdp instance for removed adapter were closed");
+}
+
+int hdp_adapter_register(DBusConnection *conn, struct btd_adapter *btd_adapter)
+{
+ const char *path = adapter_get_path(btd_adapter);
+
+ struct hdp_adapter *adapter;
+ adapter = g_new0(struct hdp_adapter, 1);
+
+ DBG("HDP init");
+ if (!g_dbus_register_interface(conn, path, HEALTH_MANAGER_INTERFACE,
+ hdp_methods, NULL, NULL,
+ adapter, hdp_path_unregister)) {
+ error("Failed to register %s interface to %s",
+ HEALTH_MANAGER_INTERFACE, path);
+ g_free(adapter);
+ return -1;
+ }
+ adapter->conn = dbus_connection_ref(conn);
+ adapter->btd_adapter = btd_adapter_ref(btd_adapter);
+ adapters = g_slist_prepend(adapters, adapter);
+ return 0;
+}
+
+void hdp_adapter_unregister(struct btd_adapter *btd_adapter)
+{
+ struct hdp_adapter *adapter;
+
+ adapter = find_adapter(adapters, btd_adapter);
+ if (!adapter)
+ return;
+
+ g_dbus_unregister_interface(adapter->conn,
+ adapter_get_path(btd_adapter),
+ HEALTH_MANAGER_INTERFACE);
+ dbus_connection_unref(adapter->conn);
+ btd_adapter_unref(adapter->btd_adapter);
+ adapters = g_slist_remove(adapters, adapter);
+ g_free(adapter);
+
+ DBG("HDP exit");
+}
diff --git a/health/hdp.h b/health/hdp.h
new file mode 100644
index 0000000..893f745
--- /dev/null
+++ b/health/hdp.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int hdp_adapter_register(DBusConnection *conn, struct btd_adapter *btd_adapter);
+void hdp_adapter_unregister(struct btd_adapter *btd_adapter);
diff --git a/health/hdp_types.h b/health/hdp_types.h
new file mode 100644
index 0000000..2db9adf
--- /dev/null
+++ b/health/hdp_types.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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
+ *
+ */
+
+
+#ifndef __HDP_TYPES_H__
+#define __HDP_TYPES_H__
+
+#include <glib.h>
+#include "mcap_lib.h"
+
+#define HDP_SERVICE_NAME "Bluez HDP"
+#define HDP_SERVICE_DSC "A Bluez health device profile implementation"
+#define HDP_SERVICE_PROVIDER "Bluez"
+
+#define HDP_VERSION 0x0100
+
+#define HDP_ERROR g_quark_from_static_string("hdp-error-quark")
+
+typedef enum {
+ HDP_DIC_PARSE_ERROR,
+ HDP_DIC_ENTRY_PARSE_ERROR,
+ HDP_UNSPECIFIED_ERROR,
+ HDP_UNKNOWN_ERROR
+} HdpError;
+
+enum data_specs {
+ DATA_EXCHANGE_SPEC_11073 = 0x01
+};
+
+typedef enum {
+ HDP_SOURCE = 0x00,
+ HDP_SINK = 0x01
+} HdpRole;
+
+struct hdp_feature {
+ guint16 dtype; /* Data type (see 5.2.9.2) */
+ gboolean dtype_present; /* Data type present in config */
+ char *dscr; /* Displayable TextName */
+};
+
+struct hdp_supp_fts {
+ guint8 mdepid; /* (0x01-0x7F) Available for use */
+ HdpRole role; /* Role (see table 5.3) */
+ gboolean role_present; /* Role present in config */
+ GSList *features; /* Feature list */
+};
+
+struct hdp_config {
+ guint8 data_spec; /* Data exchange specification */
+ GSList *supp_fts; /* Supported features list */
+ char *svc_name; /* Service name to register in SDP */
+ char *svc_dsc; /* Service description */
+ char *svc_prov; /* Service provider */
+ gboolean ds_present; /* Data spec has been assigned */
+};
+
+struct hdp_adapter {
+ struct btd_adapter *btd_adapter;
+ DBusConnection *conn; /* DBus connection */
+ GSList *instances; /* HDP instances list */
+ uint16_t ic; /* Instances counter */
+};
+
+struct hdp_instance {
+ struct hdp_adapter *adapter; /* HDP adapter */
+ struct mcap_instance *mi; /* MCAP instance */
+ uint16_t ccpsm; /* Control channel psm */
+ uint16_t dcpsm; /* Data channel psm */
+ GSList *hlink; /* Health Links */
+ uint32_t id; /* HDP instance id */
+ char *apath; /* HDP agent path */
+ char *aname; /* HDP agent name */
+ struct hdp_config *config; /* Configuration */
+ uint32_t sdp_handler; /* SDP record handler */
+};
+
+#endif /* __HDP_TYPES_H__ */
diff --git a/health/hdp_util.c b/health/hdp_util.c
new file mode 100644
index 0000000..a215d5a
--- /dev/null
+++ b/health/hdp_util.c
@@ -0,0 +1,447 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <gdbus.h>
+#include "log.h"
+#include "hdp_types.h"
+
+typedef gboolean (*parse_item_f)(DBusMessageIter *iter, GError **err,
+ gpointer user_data);
+
+struct dict_entry_func {
+ char *key;
+ parse_item_f func;
+};
+
+static gboolean check_feature(struct hdp_feature *feature)
+{
+ return feature->dtype_present;
+}
+
+static gboolean check_feature_list(struct hdp_supp_fts *fts)
+{
+ return fts->role_present && (fts->features != NULL);
+}
+
+static gboolean check_config(struct hdp_config *config)
+{
+ if (config->ds_present)
+ return config->supp_fts != NULL;
+ return TRUE;
+}
+
+static gboolean hdp_check_data_spec(guint8 data_spec)
+{
+ /* Future versions may edit this function
+ * If there are more supported data exchange specifications
+ */
+ return data_spec == DATA_EXCHANGE_SPEC_11073;
+}
+
+static void free_feature(struct hdp_feature *feature)
+{
+ if (feature->dscr) {
+ g_free(feature->dscr);
+ feature->dscr = NULL;
+ }
+ g_free(feature);
+}
+
+static void free_feature_list(struct hdp_supp_fts *fts)
+{
+ GSList *l;
+
+ for (l = fts->features; l; l = l->next)
+ free_feature(l->data);
+ g_slist_free(fts->features);
+ fts->features = NULL;
+ g_free(fts);
+}
+
+static void free_config(struct hdp_config *config)
+{
+ GSList *l;
+
+ for (l = config->supp_fts; l; l = l->next)
+ free_feature_list(l->data);
+ g_slist_free(config->supp_fts);
+ config->supp_fts = NULL;
+ if (config->svc_dsc) {
+ g_free(config->svc_dsc);
+ config->svc_dsc = NULL;
+ }
+ if (config->svc_name) {
+ g_free(config->svc_name);
+ config->svc_name = NULL;
+ }
+ if (config->svc_prov) {
+ g_free(config->svc_prov);
+ config->svc_prov = NULL;
+ }
+ g_free(config);
+}
+
+static gboolean parse_dict_entry(struct dict_entry_func dict_context[],
+ DBusMessageIter *iter,
+ GError **err,
+ gpointer user_data)
+{
+ DBusMessageIter entry;
+ char *key;
+ int ctype, i;
+ struct dict_entry_func df;
+
+ dbus_message_iter_recurse(iter, &entry);
+ ctype = dbus_message_iter_get_arg_type(&entry);
+ if (ctype != DBUS_TYPE_STRING) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+ "Dictionary entries should have a string as key");
+ return FALSE;
+ }
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+ /* Find function and call it */
+ for (i = 0, df = dict_context[0]; df.key; i++, df = dict_context[i]) {
+ if (g_strcmp0(df.key, key) == 0) {
+ return df.func(&entry, err, user_data);
+ }
+ }
+ g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+ "No function found for parsing value for key %s", key);
+ return FALSE;
+}
+
+static gboolean parse_dict(struct dict_entry_func dict_context[],
+ DBusMessageIter *iter,
+ GError **err,
+ gpointer user_data)
+{
+ int ctype;
+ DBusMessageIter dict;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ if (ctype != DBUS_TYPE_ARRAY) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR,
+ "Dictionary should be an array");
+ return FALSE;
+ }
+ dbus_message_iter_recurse(iter, &dict);
+ while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+ DBUS_TYPE_INVALID) {
+ if (ctype != DBUS_TYPE_DICT_ENTRY) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR,
+ "Dictionary array should "
+ "contain dict entries");
+ return FALSE;
+ }
+ /* Start parsing entry */
+ if (!parse_dict_entry(dict_context, &dict, err,
+ user_data))
+ return FALSE;
+ /* Finish entry parsing */
+ dbus_message_iter_next(&dict);
+ }
+ return TRUE;
+}
+
+static gboolean parse_description(DBusMessageIter *iter, GError **err,
+ gpointer data)
+{
+ struct hdp_feature *feat = data;
+ DBusMessageIter *string, variant;
+ int ctype;
+ const char *desc;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ string = iter;
+ if (ctype == DBUS_TYPE_VARIANT) {
+ /* Get value inside the variable */
+ dbus_message_iter_recurse(iter, &variant);
+ ctype = dbus_message_iter_get_arg_type(&variant);
+ string = &variant;
+ }
+
+ if (ctype != DBUS_TYPE_STRING) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+ "Value data spec should be variable or string");
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(string, &desc);
+ feat->dscr = g_strdup(desc);
+ return TRUE;
+}
+
+static gboolean parse_data_type(DBusMessageIter *iter, GError **err,
+ gpointer data)
+{
+ struct hdp_feature *feat = data;
+ DBusMessageIter *value, variant;
+ int ctype;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ value = iter;
+ if (ctype == DBUS_TYPE_VARIANT) {
+ /* Get value inside the variable */
+ dbus_message_iter_recurse(iter, &variant);
+ ctype = dbus_message_iter_get_arg_type(&variant);
+ value = &variant;
+ }
+
+ if (ctype != DBUS_TYPE_UINT16) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+ "Final value for data type should be a uint16");
+ return FALSE;
+ }
+ dbus_message_iter_get_basic(value, &feat->dtype);
+
+ /*
+ * This data should be check by the application layer because it
+ * depends on the data_spec values and is specific for each value
+ */
+ feat->dtype_present = TRUE;
+
+ return TRUE;
+}
+
+static struct dict_entry_func specs_context[] = {
+ {"data_type", parse_data_type},
+ {"description", parse_description},
+ {NULL, NULL}
+};
+
+static gboolean parse_specs(DBusMessageIter *iter, GError **err, gpointer data)
+{
+ struct hdp_supp_fts *fts = data;
+ struct hdp_feature *feature = NULL;
+ DBusMessageIter *array, value, dict;
+ int ctype;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ array = iter;
+ if (ctype == DBUS_TYPE_VARIANT) {
+ /* Get value inside the variable */
+ dbus_message_iter_recurse(iter, &value);
+ ctype = dbus_message_iter_get_arg_type(&value);
+ array = &value;
+ }
+ if (ctype != DBUS_TYPE_ARRAY) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+ "Value specs should be variable or array");
+ return FALSE;
+ }
+ dbus_message_iter_recurse(array, &dict);
+ while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+ DBUS_TYPE_INVALID){
+ feature = g_new0(struct hdp_feature, 1);
+ if (!parse_dict(specs_context, &dict, err, feature))
+ goto error;
+ if (!check_feature(feature)) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+ "Field \"data_type\" is mandatory");
+ goto error;
+ }
+ fts->features = g_slist_append(fts->features, feature);
+ dbus_message_iter_next(&dict);
+ }
+
+ return TRUE;
+error:
+ if (feature)
+ free_feature(feature);
+ return FALSE;
+}
+
+static gboolean parse_role(DBusMessageIter *iter, GError **err, gpointer data)
+{
+ struct hdp_supp_fts *fts = data;
+ DBusMessageIter value;
+ DBusMessageIter *string;
+ int ctype;
+ const char *role;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ string = iter;
+ if (ctype == DBUS_TYPE_VARIANT) {
+ /* Get value inside the variable */
+ dbus_message_iter_recurse(iter, &value);
+ ctype = dbus_message_iter_get_arg_type(&value);
+ string = &value;
+ }
+
+ if (ctype != DBUS_TYPE_STRING) {
+ g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+ "Value data spec should be variable or string");
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(string, &role);
+ if (g_strcmp0(role, "sink") == 0)
+ fts->role = HDP_SINK;
+ else if (g_strcmp0(role, "source") == 0)
+ fts->role = HDP_SOURCE;
+ else {
+ g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+ "Role value should be \"source\" or \"sink\"");
+ return FALSE;
+ }
+ fts->role_present = TRUE;
+ return TRUE;
+}
+
+static struct dict_entry_func end_point_context[] = {
+ {"role", parse_role},
+ {"specs", parse_specs},
+ {NULL, NULL}
+};
+
+static gboolean parse_end_points(DBusMessageIter *iter, GError **err,
+ gpointer data)
+{
+ struct hdp_config *config = data;
+ struct hdp_supp_fts *fts = NULL;
+ DBusMessageIter array, dict;
+ int ctype;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ if (ctype != DBUS_TYPE_VARIANT) {
+ g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+ "Value for end points should be variable");
+ return FALSE;
+ }
+ dbus_message_iter_recurse(iter, &array);
+ ctype = dbus_message_iter_get_arg_type(&array);
+ if (ctype != DBUS_TYPE_ARRAY) {
+ g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+ "Value end_point should be array inside variable");
+ return FALSE;
+ }
+
+ dbus_message_iter_recurse(&array, &dict);
+ while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+ DBUS_TYPE_INVALID){
+
+ if (ctype != DBUS_TYPE_ARRAY) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR,
+ "Dictionary should be an array");
+ return FALSE;
+ }
+ fts = g_new0(struct hdp_supp_fts, 1);
+ if (!parse_dict(end_point_context, &dict, err, fts))
+ goto error;
+ if (!check_feature_list(fts)) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR,
+ "Role field and specs are mandatory");
+ goto error;
+ }
+ config->supp_fts = g_slist_append(config->supp_fts, fts);
+ dbus_message_iter_next(&dict);
+ }
+ return TRUE;
+error:
+ if (fts)
+ free_feature_list(fts);
+ return FALSE;
+}
+
+static gboolean parse_data_spec(DBusMessageIter *iter, GError **err,
+ gpointer data)
+{
+ struct hdp_config *config = data;
+ DBusMessageIter value;
+ int ctype;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ if (ctype != DBUS_TYPE_VARIANT) {
+ g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+ "Value data spec should be variable");
+ return FALSE;
+ }
+ dbus_message_iter_recurse(iter, &value);
+ ctype = dbus_message_iter_get_arg_type(&value);
+ if (ctype != DBUS_TYPE_BYTE) {
+ g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+ "Final value data spec should be byte");
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&value, &config->data_spec);
+
+ if (!hdp_check_data_spec(config->data_spec))
+ return FALSE;
+ config->ds_present = TRUE;
+ return TRUE;
+}
+
+static struct dict_entry_func main_context[] = {
+ {"data_spec", parse_data_spec},
+ {"end_points", parse_end_points},
+ {NULL, NULL}
+};
+
+static void print_feature(gpointer elem, gpointer data)
+{
+ struct hdp_feature *feat = elem;
+
+ DBG(" Feature:");
+ DBG(" description: %s", feat->dscr);
+ DBG(" data type: %u", feat->dtype);
+}
+
+static void print_features(gpointer elem, gpointer data)
+{
+ struct hdp_supp_fts *fts = elem;
+
+ DBG("Mdep:");
+ DBG(" mdepid %u", fts->mdepid);
+ DBG(" role %d", fts->role);
+ g_slist_foreach(fts->features, print_feature, NULL);
+}
+
+struct hdp_config *hdp_get_config(DBusMessageIter *iter, GError **err)
+{
+ struct hdp_config *config;
+
+ config = g_new0(struct hdp_config, 1);
+
+ if (!parse_dict(main_context, iter, err, config))
+ goto error;
+
+ /* TODO check config */
+ if (!check_config(config)) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+ "\"data_spec\" and \"end_point\" should be set or not");
+ goto error;
+ }
+ if (!config->ds_present)
+ goto error;
+ DBG("config->data_spec %d", config->data_spec);
+ g_slist_foreach(config->supp_fts, print_features, NULL);
+ return config;
+error:
+ if (config)
+ free_config(config);
+ return NULL;
+}
diff --git a/health/hdp_util.h b/health/hdp_util.h
new file mode 100644
index 0000000..f09e9a6
--- /dev/null
+++ b/health/hdp_util.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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
+ *
+ */
+
+#ifndef __HDP_UTIL_H__
+#define __HDP_UTIL_H__
+
+#include <gdbus.h>
+#include "hdp_types.h"
+
+struct hdp_config *hdp_get_config(DBusMessageIter *iter, GError **err);
+
+#endif /* __HDP_UTIL_H__ */
diff --git a/health/main.c b/health/main.c
new file mode 100644
index 0000000..6ece69b
--- /dev/null
+++ b/health/main.c
@@ -0,0 +1,60 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "manager.h"
+
+static DBusConnection *connection;
+
+static int hdp_init(void)
+{
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (connection == NULL)
+ return -EIO;
+
+ if (hdp_manager_init(connection) < 0) {
+ dbus_connection_unref(connection);
+ return -EIO;
+ }
+ return 0;
+}
+
+static void hdp_exit(void)
+{
+ hdp_manager_exit();
+
+ dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(health, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, hdp_init, hdp_exit)
diff --git a/health/manager.c b/health/manager.c
new file mode 100644
index 0000000..dc354f8
--- /dev/null
+++ b/health/manager.c
@@ -0,0 +1,101 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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 <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "adapter.h"
+#include "device.h"
+
+#include "log.h"
+#include "manager.h"
+#include "hdp.h"
+
+#define HDP_UUID "00001400-0000-1000-8000-00805F9B34FB"
+#define HDP_SOURCE_UUID "00001401-0000-1000-8000-00805F9B34FB"
+#define HDP_SINK_UUID "00001402-0000-1000-8000-00805F9B34FB"
+
+static DBusConnection *connection = NULL;
+
+static int hdp_adapter_probe(struct btd_adapter *adapter)
+{
+ return hdp_adapter_register(connection, adapter);
+}
+
+static void hdp_adapter_remove(struct btd_adapter *adapter)
+{
+ hdp_adapter_unregister(adapter);
+}
+
+static struct btd_adapter_driver hdp_adapter_driver = {
+ .name = "hdp-adapter-driver",
+ .probe = hdp_adapter_probe,
+ .remove = hdp_adapter_remove,
+};
+
+static int hdp_driver_probe(struct btd_device *device, GSList *uuids)
+{
+ DBG("hdp driver probe");
+ return 0;
+}
+
+static void hdp_driver_remove(struct btd_device *device)
+{
+ DBG("hdp driver remove");
+}
+
+static struct btd_device_driver hdp_device_driver = {
+ .name = "hdp_device-driver",
+ .uuids = BTD_UUIDS(HDP_UUID, HDP_SOURCE_UUID, HDP_SINK_UUID),
+ .probe = hdp_driver_probe,
+ .remove = hdp_driver_remove,
+};
+
+int hdp_manager_init(DBusConnection *conn)
+{
+ connection = dbus_connection_ref(conn);
+
+ btd_register_adapter_driver(&hdp_adapter_driver);
+ btd_register_device_driver(&hdp_device_driver);
+
+ DBG("hdp manager init");
+ return 0;
+}
+
+void hdp_manager_exit(void)
+{
+ btd_unregister_device_driver(&hdp_device_driver);
+ btd_unregister_adapter_driver(&hdp_adapter_driver);
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+ DBG("hdp manager exit");
+}
diff --git a/health/manager.h b/health/manager.h
new file mode 100644
index 0000000..b91ef75
--- /dev/null
+++ b/health/manager.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int hdp_manager_init(DBusConnection *conn);
+void hdp_manager_exit(void);
--
1.6.3.3


Subject: [PATCH 24/32] Disconnect health link petition

---
health/hdp.c | 28 +++++++++++++++-------------
1 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index ed26fe5..f29a1c5 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -401,13 +401,10 @@ static void health_link_free(struct hdp_link *hdpl)
if (hdpl->mcl) {
mcap_close_mcl(hdpl->mcl, FALSE);
mcap_mcl_unref(hdpl->mcl);
- hdpl->mcl = NULL;
}

- if (hdpl->path) {
+ if (hdpl->path)
g_free(hdpl->path);
- hdpl->path = NULL;
- }

hdpl->hdpi->hlink = g_slist_remove(hdpl->hdpi->hlink, hdpl);
g_free(hdpl);
@@ -623,32 +620,37 @@ static DBusMessage *hdp_disconnect(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
struct hdp_device *device = user_data;
- struct hdp_link *hlink;
+ struct hdp_link *hdpl;
const char *path;
- const gboolean *del;
+ gboolean cache;

if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_BOOLEAN, &del,
- DBUS_TYPE_INVALID)){
+ DBUS_TYPE_BOOLEAN, &cache,
+ DBUS_TYPE_INVALID)){
return g_dbus_create_error(msg,
ERROR_INTERFACE ".InvalidArguments",
"Invalid arguments in method call");
}

- hlink = find_health_link(device->hdp_adapter, path);
- if (!hlink)
+ hdpl = find_health_link(device->hdp_adapter, path);
+ if (!hdpl)
return g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
"Health link does not found");

- return g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
- "Not yet implemented");
+ if (cache) {
+ mcap_close_mcl(hdpl->mcl, cache);
+ hdpl->closed = TRUE;
+ } else
+ g_dbus_unregister_interface(conn, hdpl->path, HEALTH_LINK);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}

static GDBusMethodTable device_methods[] = {
{ "GetHealthInstances", "", "aa{sv}", get_health_instances,
G_DBUS_METHOD_FLAG_ASYNC },
{ "Connect", "uu", "o", hdp_connect, G_DBUS_METHOD_FLAG_ASYNC },
- { "Diconnect", "ob", "", hdp_disconnect },
+ { "Disconnect", "ob", "", hdp_disconnect },
{ NULL }
};

--
1.6.3.3


Subject: [PATCH 28/32] Changed HEALTH_MANAGER_INTERFACE to HEALTH_MANAGER

---
health/hdp.c | 6 +++---
health/hdp_types.h | 7 ++++---
2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index bb03ffa..58d0394 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -964,11 +964,11 @@ int hdp_adapter_register(DBusConnection *conn, struct btd_adapter *btd_adapter)
adapter = g_new0(struct hdp_adapter, 1);

DBG("HDP init");
- if (!g_dbus_register_interface(conn, path, HEALTH_MANAGER_INTERFACE,
+ if (!g_dbus_register_interface(conn, path, HEALTH_MANAGER,
hdp_methods, NULL, NULL,
adapter, hdp_path_unregister)) {
error("Failed to register %s interface to %s",
- HEALTH_MANAGER_INTERFACE, path);
+ HEALTH_MANAGER, path);
g_free(adapter);
return -1;
}
@@ -988,7 +988,7 @@ void hdp_adapter_unregister(struct btd_adapter *btd_adapter)

g_dbus_unregister_interface(adapter->conn,
adapter_get_path(btd_adapter),
- HEALTH_MANAGER_INTERFACE);
+ HEALTH_MANAGER);
dbus_connection_unref(adapter->conn);
btd_adapter_unref(adapter->btd_adapter);
adapters = g_slist_remove(adapters, adapter);
diff --git a/health/hdp_types.h b/health/hdp_types.h
index abbfbf3..aff116e 100644
--- a/health/hdp_types.h
+++ b/health/hdp_types.h
@@ -42,9 +42,10 @@

#define HDP_ERROR g_quark_from_static_string("hdp-error-quark")

-#define HEALTH_MANAGER_INTERFACE "org.bluez.HealthAdapter"
-#define HEALTH_DEVICE "org.bluez.HealthDevice"
-#define HEALTH_LINK "org.bluez.HealthLink"
+#define HEALTH_MANAGER "org.bluez.HealthAdapter"
+#define HEALTH_DEVICE "org.bluez.HealthDevice"
+#define HEALTH_LINK "org.bluez.HealthLink"
+#define HEALTH_AGENT "org.bluez.HealthAgent"

#define HDP_NO_PREFERENCE_DC 0x00
#define HDP_RELIABLE_DC 0x01
--
1.6.3.3


Subject: [PATCH 18/32] Unify the creation of health links

---
health/hdp.c | 82 ++++++++++++++++++++++++++++++++++++---------------------
1 files changed, 52 insertions(+), 30 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index d38aff1..eb673bd 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -369,28 +369,17 @@ static GDBusMethodTable health_link_methods[] = {
{ NULL }
};

-static void hdp_mcl_connect_cb(struct mcap_mcl *mcl, GError *err, void *data)
+static struct hdp_link *create_health_link(struct hdp_instance *hdpi,
+ struct mcap_mcl *mcl, GError **err)
{
- struct hdp_connection_cb *cb_data = data;
- struct hdp_device *device = cb_data->device;
- struct hdp_instance *hdpi = cb_data->hdpi;
- DBusMessage *msg = cb_data->msg;
- struct hdp_link *hdpl = NULL;
- GError *cberr = NULL;
- DBusMessage *reply;
-
- g_free(cb_data);
-
- if (err)
- goto fail;
+ struct hdp_link *hdpl;

hdpl = g_new0(struct hdp_link, 1);
hdpl->hdpi = hdpi;
- hdpl->dev = device;
hdpl->mcl = mcap_mcl_ref(mcl);
set_health_link_path(hdpl);

- mcap_mcl_set_cb(mcl, &cberr, hdpl,
+ mcap_mcl_set_cb(mcl, err, hdpl,
MCAP_MDL_CB_CONNECTED, hdp_mcap_mdl_connected_cb,
MCAP_MDL_CB_CLOSED, hdp_mcap_mdl_closed_cb,
MCAP_MDL_CB_DELETED, hdp_mcap_mdl_deleted_cb,
@@ -399,16 +388,46 @@ static void hdp_mcl_connect_cb(struct mcap_mcl *mcl, GError *err, void *data)
MCAP_MDL_CB_REMOTE_RECONN_REQ, hdp_mcap_mdl_reconn_req_cb,
MCAP_MDL_CB_INVALID);

- if (cberr)
- goto fail;
+ if (*err)
+ return NULL;

- if (!g_dbus_register_interface(hdpl->dev->conn, hdpl->path, HEALTH_LINK,
+ if (g_dbus_register_interface(hdpl->dev->conn, hdpl->path, HEALTH_LINK,
health_link_methods, NULL, NULL,
- hdpl, health_link_path_unregister)) {
- error("D-Bus failed to register %s interface to %s",
+ hdpl, health_link_path_unregister))
+ return hdpl;
+
+ g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+ "Cant register the health link in the bus");
+
+ /* TODO create a function to free health link correctly */
+ /* MCAP will close the MCL and won't cache it if we didn't
+ * increase the MCL reference counter during the callback. */
+ mcap_mcl_unref(hdpl->mcl);
+ g_free(hdpl->path);
+ g_free(hdpl);
+ error("D-Bus failed to register %s interface to %s",
HEALTH_LINK, hdpl->path);
+ return NULL;
+}
+
+static void hdp_mcl_connect_cb(struct mcap_mcl *mcl, GError *err, void *data)
+{
+ struct hdp_connection_cb *cb_data = data;
+ struct hdp_device *device = cb_data->device;
+ struct hdp_instance *hdpi = cb_data->hdpi;
+ DBusMessage *msg = cb_data->msg;
+ struct hdp_link *hdpl = NULL;
+ GError *gerr = NULL;
+ DBusMessage *reply;
+
+ g_free(cb_data);
+
+ if (err)
+ goto fail;
+
+ hdpl = create_health_link(hdpi, mcl, &gerr);
+ if (gerr)
goto fail;
- }

hdpi->hlink = g_slist_prepend(hdpi->hlink, hdpl);
reply = g_dbus_create_reply(msg, DBUS_TYPE_OBJECT_PATH, &hdpl->path,
@@ -417,14 +436,9 @@ static void hdp_mcl_connect_cb(struct mcap_mcl *mcl, GError *err, void *data)
return;
fail:
reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HdpError",
- (err ? err->message : cberr->message));
- if (cberr) {
- /* MCAP will close the MCL and won't cache it if we didn't
- * increase the MCL reference counter during the callback. */
- mcap_mcl_unref(hdpl->mcl);
- g_free(hdpl->path);
- g_free(hdpl);
- g_error_free(cberr);
+ (err ? err->message : gerr->message));
+ if (gerr) {
+ g_error_free(gerr);
}

g_dbus_send_message(device->conn, reply);
@@ -578,8 +592,16 @@ static void client_disconnected(DBusConnection *connection, void *user_data)

static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
{
- /* struct hdp_instance *hdpi = data; */
+ struct hdp_instance *hdpi = data;
+ struct hdp_link *hdpl;
+ GError *err = NULL;
+
DBG("TODO: implement mcl_connected");
+ hdpl = create_health_link(hdpi, mcl, &err);
+ if (err)
+ return;
+ /* TODO: Send the notification to the Agent */
+ hdpi->hlink = g_slist_prepend(hdpi->hlink, hdpl);
}

static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
--
1.6.3.3


Subject: [PATCH 29/32] Call the agent when a new Health Link is created

---
health/hdp.c | 27 ++++++++++++++++++++++++---
src/bluetooth.conf | 1 +
2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 58d0394..1fc1cb9 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -760,14 +760,35 @@ static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
{
struct hdp_instance *hdpi = data;
struct hdp_link *hdpl;
+ DBusMessage* message;
GError *err = NULL;

DBG("mcl_connected");
+
+ message = dbus_message_new_method_call(hdpi->aname, hdpi->apath,
+ HEALTH_AGENT, "LinkConnected");
+ if (!message) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
hdpl = create_health_link(hdpi, mcl, &err);
- if (err)
+ if (err) {
+ dbus_message_unref(message);
return;
- /* TODO: Send the notification to the Agent */
- hdpi->hlink = g_slist_prepend(hdpi->hlink, hdpl);
+ }
+
+ dbus_message_append_args(message,
+ DBUS_TYPE_OBJECT_PATH, &hdpl->path,
+ DBUS_TYPE_INVALID);
+
+ if (g_dbus_send_message(hdpi->adapter->conn, message)) {
+ hdpi->hlink = g_slist_prepend(hdpi->hlink, hdpl);
+ return;
+ }
+ error("D-Bus send failed");
+ health_link_free(hdpl);
+ dbus_message_unref(message);
}

static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
diff --git a/src/bluetooth.conf b/src/bluetooth.conf
index 56e7a83..4471da5 100644
--- a/src/bluetooth.conf
+++ b/src/bluetooth.conf
@@ -12,6 +12,7 @@
<allow send_destination="org.bluez"/>
<allow send_interface="org.bluez.Agent"/>
<allow send_interface="org.bluez.HandsfreeAgent"/>
+ <allow send_interface="org.bluez.HealthAgent"/>
</policy>

<policy at_console="true">
--
1.6.3.3


Subject: [PATCH 32/32] Add support for mcl reconnections

---
health/hdp.c | 20 ++++++++++++--------
1 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 6a73e05..8a6a0d4 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -56,6 +56,7 @@ struct hdp_connection_cb {
struct hdp_instance *hdpi;
uint32_t rem_id;
DBusMessage *msg;
+ struct hdp_link *hdpl;
};

static struct hdp_adapter *find_adapter(GSList *list,
@@ -554,11 +555,15 @@ static void hdp_mcl_connect_cb(struct mcap_mcl *mcl, GError *err, void *data)
if (err)
goto fail;

- hdpl = create_health_link(hdpi, mcl, &gerr);
- if (gerr)
- goto fail;
+ if (cb_data->hdpl)
+ hdpl = cb_data->hdpl;
+ else {
+ hdpl = create_health_link(hdpi, mcl, &gerr);
+ if (gerr)
+ goto fail;
+ hdpi->hlink = g_slist_prepend(hdpi->hlink, hdpl);
+ }

- hdpi->hlink = g_slist_prepend(hdpi->hlink, hdpl);
reply = g_dbus_create_reply(msg, DBUS_TYPE_OBJECT_PATH, &hdpl->path,
DBUS_TYPE_INVALID);
g_dbus_send_message(device->conn, reply);
@@ -664,10 +669,7 @@ static DBusMessage *hdp_connect(DBusConnection *conn,
hdpi = l->data;
hdpl = get_health_link(hdpi, device);
if (hdpl) {
- if (hdpl->closed) {
- DBG("Need a reconection");
- /* TODO: Reconnect */;
- } else
+ if (!hdpl->closed)
return g_dbus_create_reply(msg, DBUS_TYPE_OBJECT_PATH,
&hdpl->path, DBUS_TYPE_INVALID);
}
@@ -677,6 +679,8 @@ static DBusMessage *hdp_connect(DBusConnection *conn,
cb_data->hdpi = hdpi;
cb_data->rem_id = rid;
cb_data->msg = dbus_message_ref(msg);
+ /* Used for reconnections */
+ cb_data->hdpl = hdpl;

adapter = device->hdp_adapter->btd_adapter;
adapter_get_address(adapter, &src);
--
1.6.3.3


Subject: [PATCH 26/32] Implement connect data channel callback

---
health/hdp.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++----
health/hdp_types.h | 5 ++++
2 files changed, 67 insertions(+), 6 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index f29a1c5..a480b8e 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -107,6 +107,13 @@ static struct hdp_link *find_health_link(struct hdp_adapter *adapter,
return NULL;
}

+static int hdp_supp_fts_mdepcmp(gconstpointer ft, gconstpointer mdepid)
+{
+ const struct hdp_supp_fts *fts = ft;
+
+ return bcmp(&fts->mdepid, mdepid, sizeof(uint8_t));
+}
+
static int hdp_instance_idcmp(gconstpointer instance, gconstpointer p)
{
const struct hdp_instance *hdpi = instance;
@@ -379,19 +386,65 @@ static void hdp_mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data)
DBG("TODO: Incomplete callback, mdl closed");
}

-static uint8_t hdp_mcap_mdl_conn_req_cb(struct mcap_mdl *mdl, void *data)
+static uint8_t hdp_mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
{
/* struct hdp_link *hdpl = data; */
DBG("TODO: Incomplete callback, mdl connection request");
return MCAP_REQUEST_NOT_SUPPORTED;
}

-static uint8_t hdp_mcap_mdl_reconn_req_cb(struct mcap_mcl *mcl,
- uint8_t mdepid, uint16_t mdlid,
- uint8_t *conf, void *data)
+static uint8_t hdp_mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
+ uint16_t mdlid, uint8_t *conf, void *data)
{
- DBG("TODO: Incomplete callback, mdl reconnection request");
- return MCAP_REQUEST_NOT_SUPPORTED;
+ struct hdp_link *hdpl = data;
+ struct hdp_supp_fts *f;
+ struct hdp_dc *dc;
+ uint8_t new_conf;
+ GSList *l;
+
+ l = g_slist_find_custom(hdpl->hdpi->config->supp_fts, &mdepid,
+ hdp_supp_fts_mdepcmp);
+ if (!l)
+ return MCAP_INVALID_MDEP;
+ f = l->data;
+
+ /* Check if is the first dc if so,
+ * only reliable configuration is allowed */
+ switch(*conf) {
+ case HDP_NO_PREFERENCE_DC:
+ if (f->role == HDP_SINK)
+ return MCAP_CONFIGURATION_REJECTED;
+ else
+ new_conf = HDP_RELIABLE_DC;
+ break;
+ case HDP_STREAMING_DC:
+ if (g_slist_length(hdpl->channels) == 0)
+ return MCAP_CONFIGURATION_REJECTED;
+ case HDP_RELIABLE_DC:
+ if (f->role == HDP_SOURCE)
+ return MCAP_CONFIGURATION_REJECTED;
+ new_conf = *conf;
+ break;
+ default:
+ /* Special case defined in HDP spec 3.4. When an invalid
+ * configuration is received we need close the MCL when
+ * we are still processing the callback. When MCL is
+ * closed in a callback the returned value won't be
+ * proccesed in MCAP */
+ /* TODO: Send MCL disconnection to agent */
+ g_dbus_unregister_interface(hdpl->hdpi->adapter->conn,
+ hdpl->path, HEALTH_LINK);
+ return MCAP_CONFIGURATION_REJECTED; /* not processed */
+ }
+ *conf = new_conf;
+
+ dc = g_new0(struct hdp_dc, 1);
+ dc->hdpl = hdpl;
+ dc->conf = *conf;
+ dc->mdlid = mdlid;
+
+ hdpl->ndc = dc;
+ return MCAP_SUCCESS;
}

static void health_link_free(struct hdp_link *hdpl)
@@ -406,6 +459,9 @@ static void health_link_free(struct hdp_link *hdpl)
if (hdpl->path)
g_free(hdpl->path);

+ if (hdpl->ndc)
+ g_free(hdpl->ndc);
+
hdpl->hdpi->hlink = g_slist_remove(hdpl->hdpi->hlink, hdpl);
g_free(hdpl);
}
diff --git a/health/hdp_types.h b/health/hdp_types.h
index 6d41e2b..abbfbf3 100644
--- a/health/hdp_types.h
+++ b/health/hdp_types.h
@@ -46,6 +46,10 @@
#define HEALTH_DEVICE "org.bluez.HealthDevice"
#define HEALTH_LINK "org.bluez.HealthLink"

+#define HDP_NO_PREFERENCE_DC 0x00
+#define HDP_RELIABLE_DC 0x01
+#define HDP_STREAMING_DC 0x02
+
typedef enum {
HDP_DIC_PARSE_ERROR,
HDP_DIC_ENTRY_PARSE_ERROR,
@@ -113,6 +117,7 @@ struct hdp_link {
GSList *channels; /* Data channels */
char *path; /* HDP link path */
uint32_t id; /* Health link id */
+ struct hdp_dc *ndc; /* Data channel negotiated */
};

struct hdp_dc {
--
1.6.3.3


Subject: [PATCH 31/32] Send MCL disconnect callback to agents

From: José Antonio Santos Cadenas <[email protected]>

Whenever a MCL is discoinnected we shall notify to agents using
D-Bus callback.
---
health/hdp.c | 82 +++++++++++++++++++++++++++++++++++++--------------------
1 files changed, 53 insertions(+), 29 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 1fc1cb9..6a73e05 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -393,10 +393,46 @@ static uint8_t hdp_mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
return MCAP_REQUEST_NOT_SUPPORTED;
}

+static void health_link_free(struct hdp_link *hdpl)
+{
+ /*TODO: Release structures related with Data Channels */
+
+ if (hdpl->mcl) {
+ mcap_close_mcl(hdpl->mcl, FALSE);
+ mcap_mcl_unref(hdpl->mcl);
+ }
+
+ if (hdpl->path)
+ g_free(hdpl->path);
+
+ if (hdpl->ndc)
+ g_free(hdpl->ndc);
+
+ g_free(hdpl);
+}
+
+static gboolean agent_mcl_disconnect_msg(struct hdp_link *hdpl)
+{
+ struct hdp_instance *hdpi = hdpl->hdpi;
+ DBusMessage* message;
+
+ message = dbus_message_new_method_call(hdpi->aname, hdpi->apath,
+ HEALTH_AGENT, "LinkDisconnected");
+ if (!message) {
+ error("Couldn't allocate D-Bus message");
+ return FALSE;
+ }
+
+ dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &hdpl->path,
+ DBUS_TYPE_INVALID);
+ return g_dbus_send_message(hdpi->adapter->conn, message);
+}
+
static uint8_t hdp_mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
uint16_t mdlid, uint8_t *conf, void *data)
{
struct hdp_link *hdpl = data;
+ struct hdp_instance *hdpi;
struct hdp_supp_fts *f;
struct hdp_dc *dc;
uint8_t new_conf;
@@ -418,7 +454,7 @@ static uint8_t hdp_mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
new_conf = HDP_RELIABLE_DC;
break;
case HDP_STREAMING_DC:
- if (g_slist_length(hdpl->channels) == 0)
+ if (!hdpl->channels)
return MCAP_CONFIGURATION_REJECTED;
case HDP_RELIABLE_DC:
if (f->role == HDP_SOURCE)
@@ -427,13 +463,16 @@ static uint8_t hdp_mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
break;
default:
/* Special case defined in HDP spec 3.4. When an invalid
- * configuration is received we need close the MCL when
+ * configuration is received we shall close the MCL when
* we are still processing the callback. When MCL is
* closed in a callback the returned value won't be
* proccesed in MCAP */
- /* TODO: Send MCL disconnection to agent */
- g_dbus_unregister_interface(hdpl->hdpi->adapter->conn,
- hdpl->path, HEALTH_LINK);
+ hdpi = hdpl->hdpi;
+ hdpi->hlink = g_slist_remove(hdpi->hlink, hdpl);
+ agent_mcl_disconnect_msg(hdpl);
+ if (!g_dbus_unregister_interface(hdpi->adapter->conn,
+ hdpl->path, HEALTH_LINK))
+ health_link_free(hdpl);
return MCAP_CONFIGURATION_REJECTED; /* not processed */
}
*conf = new_conf;
@@ -447,25 +486,6 @@ static uint8_t hdp_mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
return MCAP_SUCCESS;
}

-static void health_link_free(struct hdp_link *hdpl)
-{
- /*TODO: Release structures related with Data Channels */
-
- if (hdpl->mcl) {
- mcap_close_mcl(hdpl->mcl, FALSE);
- mcap_mcl_unref(hdpl->mcl);
- }
-
- if (hdpl->path)
- g_free(hdpl->path);
-
- if (hdpl->ndc)
- g_free(hdpl->ndc);
-
- hdpl->hdpi->hlink = g_slist_remove(hdpl->hdpi->hlink, hdpl);
- g_free(hdpl);
-}
-
static void health_link_path_unregister(void *data)
{
struct hdp_link *hdpl = data;
@@ -696,8 +716,11 @@ static DBusMessage *hdp_disconnect(DBusConnection *conn,
if (cache) {
mcap_close_mcl(hdpl->mcl, cache);
hdpl->closed = TRUE;
- } else
- g_dbus_unregister_interface(conn, hdpl->path, HEALTH_LINK);
+ } else {
+ hdpl->hdpi->hlink = g_slist_remove(hdpl->hdpi->hlink, hdpl);
+ if (!g_dbus_unregister_interface(conn, hdpl->path, HEALTH_LINK))
+ health_link_free(hdpl);
+ }

return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
@@ -788,7 +811,6 @@ static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
}
error("D-Bus send failed");
health_link_free(hdpl);
- dbus_message_unref(message);
}

static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
@@ -837,8 +859,10 @@ static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
hdpl = l->data;

hdpi->hlink = g_slist_remove(hdpi->hlink, hdpl);
- g_dbus_unregister_interface(hdpi->adapter->conn, hdpl->path,
- HEALTH_LINK);
+ agent_mcl_disconnect_msg(hdpl);
+ if (!g_dbus_unregister_interface(hdpi->adapter->conn, hdpl->path,
+ HEALTH_LINK))
+ health_link_free(hdpl);
}

static DBusMessage *hdp_create_instance(DBusConnection *conn,
--
1.6.3.3


Subject: [PATCH 30/32] Add a test that creates a simple health agent

---
test/test-health | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 61 insertions(+), 0 deletions(-)
create mode 100755 test/test-health

diff --git a/test/test-health b/test/test-health
new file mode 100755
index 0000000..2129923
--- /dev/null
+++ b/test/test-health
@@ -0,0 +1,61 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import dbus
+import dbus.service
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+
+agent_iface = 'org.bluez.HealthAgent'
+obj_path = "/org/bluez/test/health/agent"
+
+DBusGMainLoop(set_as_default=True)
+loop = gobject.MainLoop()
+
+bus = dbus.SystemBus()
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+hdp = dbus.Interface(bus.get_object("org.bluez", manager.DefaultAdapter()),
+ "org.bluez.HealthAdapter")
+
+class HDP(dbus.service.Object):
+ def __init__(self, bus, obj_path):
+ self.bus = bus
+ dbus.service.Object.__init__(self, self.bus, obj_path)
+ @dbus.service.method(agent_iface, in_signature='o', out_signature='',
+ sender_keyword='sender')
+ def LinkConnected(self, path, sender=None):
+ print "Connected new link %s: (sender %s)" % (path, sender)
+ @dbus.service.method(agent_iface, in_signature='o', out_signature='',
+ sender_keyword='sender')
+ def LinkDisconnected(self, path, sender=None):
+ print "Link %s: disconnected (sender: %s)" % (path, sender)
+
+session_id = hdp.CreateInstance(dbus.ObjectPath(obj_path),
+ {"data_spec": dbus.Byte(1, variant_level=1),
+ "end_points":dbus.Array([{ "role": dbus.String("sink", variant_level=1),
+ "specs": dbus.Array([{
+ "data_type":
+ dbus.UInt16(4100, variant_level =1),
+ "description":
+ dbus.String("Oximeter",
+ variant_level = 1),
+ },{
+ "data_type":
+ dbus.UInt16(4103, variant_level =1),
+ "description":
+ dbus.String("Blood pressure",
+ variant_level = 1),
+ }
+ ], variant_level=1),
+ },
+ ], variant_level=1)})
+
+hdp_obj = HDP(bus, obj_path);
+
+try:
+ print "Waiting for connections, push Ctrl+C to stop"
+ loop.run()
+except:
+ print "Loop interrupted, closing session"
--
1.6.3.3


Subject: [PATCH 27/32] Change function name when retreiving remote SDP records

---
health/hdp.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index a480b8e..bb03ffa 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -266,7 +266,7 @@ static void fill_up_health_instances(DBusMessageIter *dict, gpointer data)
}
}

-static void sink_health_instances(sdp_list_t *recs, int err, gpointer user_data)
+static void health_records_found(sdp_list_t *recs, int err, gpointer user_data)
{
struct instances_aux *cb_data = user_data;
DBusMessage *msg = cb_data->msg;
@@ -314,7 +314,7 @@ static DBusMessage *get_health_instances(DBusConnection *conn,


bt_string2uuid(&uuid, HDP_UUID);
- if (bt_search_service(&src, &dst, &uuid, sink_health_instances,
+ if (bt_search_service(&src, &dst, &uuid, health_records_found,
cb_data, NULL) == 0)
return NULL;

--
1.6.3.3


Subject: [PATCH 25/32] Create new structure to manage data channels in HDP

---
health/hdp_types.h | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/health/hdp_types.h b/health/hdp_types.h
index 5db8e0d..6d41e2b 100644
--- a/health/hdp_types.h
+++ b/health/hdp_types.h
@@ -115,6 +115,13 @@ struct hdp_link {
uint32_t id; /* Health link id */
};

+struct hdp_dc {
+ struct hdp_link *hdpl; /* Health link */
+ struct mcap_mdl *mdl; /* MCAP MDL structure */
+ uint8_t conf; /* Requested conf */
+ uint16_t mdlid; /* MDL id */
+};
+
struct hdp_device {
DBusConnection *conn; /* For name listener handling */
struct btd_device *dev; /* Device reference */
--
1.6.3.3


Subject: [PATCH 22/32] Initial work for disconnecting health links

---
health/hdp.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 45 insertions(+), 0 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 310f6ff..d6a8758 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -88,6 +88,25 @@ static struct hdp_device *find_device(GSList *devices, struct btd_device *dev)
return NULL;
}

+static struct hdp_link *find_health_link(struct hdp_adapter *adapter,
+ const char *link_path)
+{
+ struct hdp_instance *hdpi;
+ struct hdp_link *hdpl;
+ GSList *l, *ll;
+
+ for (l = adapter->instances; l; l = l->next) {
+ hdpi = l->data;
+ for (ll = hdpi->hlink; ll; ll = ll->next) {
+ hdpl = ll->data;
+ if (!strcmp(hdpl->path, link_path))
+ return hdpl;
+ }
+ }
+
+ return NULL;
+}
+
static int hdp_instance_idcmp(gconstpointer instance, gconstpointer p)
{
const struct hdp_instance *hdpi = instance;
@@ -562,10 +581,36 @@ static DBusMessage *hdp_connect(DBusConnection *conn,
"Error getting remote information");
}

+static DBusMessage *hdp_disconnect(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct hdp_device *device = user_data;
+ struct hdp_link *hlink;
+ const char *path;
+ const gboolean *del;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_BOOLEAN, &del,
+ DBUS_TYPE_INVALID)){
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ }
+
+ hlink = find_health_link(device->hdp_adapter, path);
+ if (!hlink)
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "Health link does not found");
+
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "Not yet implemented");
+}
+
static GDBusMethodTable device_methods[] = {
{ "GetHealthInstances", "", "aa{sv}", get_health_instances,
G_DBUS_METHOD_FLAG_ASYNC },
{ "Connect", "uu", "o", hdp_connect, G_DBUS_METHOD_FLAG_ASYNC },
+ { "Diconnect", "ob", "", hdp_disconnect },
{ NULL }
};

--
1.6.3.3


Subject: [PATCH 23/32] Avoid multiple links with the same device

---
health/hdp.c | 42 ++++++++++++++++++++++++++++++++++++++++--
1 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index d6a8758..ed26fe5 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -117,7 +117,8 @@ static int hdp_instance_idcmp(gconstpointer instance, gconstpointer p)
return -1;
}

-static int hdp_link_mcl_cmp(gconstpointer link, gconstpointer mcl){
+static int hdp_link_mcl_cmp(gconstpointer link, gconstpointer mcl)
+{
const struct hdp_link *hdpl = link;

if (hdpl->mcl == mcl)
@@ -125,6 +126,17 @@ static int hdp_link_mcl_cmp(gconstpointer link, gconstpointer mcl){
return -1;
}

+static int hdp_link_addr_cmp(gconstpointer link, gconstpointer addr)
+{
+ const struct hdp_link *hdpl = link;
+ const bdaddr_t *dst;
+ bdaddr_t mcl_addr;
+
+ dst = addr;
+ mcl_addr = mcap_mcl_get_addr(hdpl->mcl);
+ return bacmp(addr, &mcl_addr);
+}
+
static void set_health_link_path(struct hdp_link *hdpl)
{
char path[MAX_PATH_LENGTH + 1];
@@ -534,12 +546,27 @@ fail:
g_dbus_send_message(device->conn, reply);
}

+static struct hdp_link *get_health_link(struct hdp_instance *hdpi,
+ struct hdp_device *device)
+{
+ bdaddr_t dst;
+ GSList *l;
+
+ device_get_address(device->dev, &dst);
+ l = g_slist_find_custom(hdpi->hlink, &dst, hdp_link_addr_cmp);
+ if (l)
+ return l->data;
+ return NULL;
+}
+
static DBusMessage *hdp_connect(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
struct hdp_device *device = user_data;
struct hdp_connection_cb *cb_data;
struct btd_adapter *adapter;
+ struct hdp_instance *hdpi;
+ struct hdp_link *hdpl;
bdaddr_t src, dst;
uint32_t lid, rid;
uuid_t uuid;
@@ -561,9 +588,20 @@ static DBusMessage *hdp_connect(DBusConnection *conn,
ERROR_INTERFACE ".InvalidArguments",
"Invalid local health instance id");

+ hdpi = l->data;
+ hdpl = get_health_link(hdpi, device);
+ if (hdpl) {
+ if (hdpl->closed) {
+ DBG("Need a reconection");
+ /* TODO: Reconnect */;
+ } else
+ return g_dbus_create_reply(msg, DBUS_TYPE_OBJECT_PATH,
+ &hdpl->path, DBUS_TYPE_INVALID);
+ }
+
cb_data = g_new0(struct hdp_connection_cb, 1);
cb_data->device = device;
- cb_data->hdpi = l->data;
+ cb_data->hdpi = hdpi;
cb_data->rem_id = rid;
cb_data->msg = dbus_message_ref(msg);

--
1.6.3.3


Subject: [PATCH 19/32] Remove hdp_device pointer from health link struct

---
health/hdp.c | 6 +++---
health/hdp_types.h | 1 -
2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index eb673bd..234109a 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -391,9 +391,9 @@ static struct hdp_link *create_health_link(struct hdp_instance *hdpi,
if (*err)
return NULL;

- if (g_dbus_register_interface(hdpl->dev->conn, hdpl->path, HEALTH_LINK,
- health_link_methods, NULL, NULL,
- hdpl, health_link_path_unregister))
+ if (g_dbus_register_interface(hdpl->hdpi->adapter->conn, hdpl->path,
+ HEALTH_LINK, health_link_methods,
+ NULL, NULL, hdpl, health_link_path_unregister))
return hdpl;

g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
diff --git a/health/hdp_types.h b/health/hdp_types.h
index 05bfbfe..79419a1 100644
--- a/health/hdp_types.h
+++ b/health/hdp_types.h
@@ -104,7 +104,6 @@ struct hdp_instance {

struct hdp_link {
struct hdp_instance *hdpi; /* HDP session */
- struct hdp_device *dev; /* Health Device */
struct mcap_mcl *mcl; /* MCAP mcl */
GSList *channels; /* Data channels */
char *path; /* HDP link path */
--
1.6.3.3


Subject: [PATCH 20/32] Release health link resources

---
health/hdp.c | 27 ++++++++++++++++++++++++---
1 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 234109a..4ed0150 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -359,10 +359,32 @@ static uint8_t hdp_mcap_mdl_reconn_req_cb(struct mcap_mcl *mcl,
return MCAP_REQUEST_NOT_SUPPORTED;
}

+static void health_link_free(struct hdp_link *hdpl)
+{
+ /*TODO: Release structures related with Data Channels */
+
+ if (hdpl->mcl) {
+ mcap_close_mcl(hdpl->mcl, FALSE);
+ mcap_mcl_unref(hdpl->mcl);
+ hdpl->mcl = NULL;
+ }
+
+ if (hdpl->path) {
+ g_free(hdpl->path);
+ hdpl->path = NULL;
+ }
+
+ hdpl->hdpi->hlink = g_slist_remove(hdpl->hdpi->hlink, hdpl);
+ g_free(hdpl);
+}
+
static void health_link_path_unregister(void *data)
{
- /* struct hdp_link *hdpl = data */
- /* TODO: Unregister hdp_link*/
+ struct hdp_link *hdpl = data;
+
+ DBG("Unregistered interface %s on path %s", HEALTH_LINK, hdpl->path);
+
+ health_link_free(hdpl);
}

static GDBusMethodTable health_link_methods[] = {
@@ -399,7 +421,6 @@ static struct hdp_link *create_health_link(struct hdp_instance *hdpi,
g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
"Cant register the health link in the bus");

- /* TODO create a function to free health link correctly */
/* MCAP will close the MCL and won't cache it if we didn't
* increase the MCL reference counter during the callback. */
mcap_mcl_unref(hdpl->mcl);
--
1.6.3.3


Subject: [PATCH 12/32] Complete the response dictionary in GetHealthInstances response

---
health/hdp.c | 169 ++++++++++++++++++++++++++++++++++-------------------
health/hdp_util.c | 56 +++++++++++++++++-
health/hdp_util.h | 12 ++++
3 files changed, 175 insertions(+), 62 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 8bb6057..840af0f 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -46,6 +46,12 @@ struct instances_aux {
DBusMessage *msg;
};

+struct health_instances_aux {
+ guint32 handler;
+ guint8 data_spec;
+ GSList *end_points;
+};
+
static struct hdp_adapter *find_adapter(GSList *list,
struct btd_adapter *btd_adapter)
{
@@ -86,41 +92,114 @@ static int hdp_instance_idcmp(gconstpointer instance, gconstpointer p)
return -1;
}

-static void append_dict_features(DBusMessageIter *iter, GSList *end_points)
+static void fill_up_one_spec(DBusMessageIter *dict, gpointer data)
{
- DBusMessageIter entry, array;
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY,
- NULL, &entry);
- dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, "end_points");
-
- dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
- DBUS_TYPE_VARIANT_AS_STRING
- DBUS_TYPE_ARRAY_AS_STRING
- DBUS_TYPE_ARRAY_AS_STRING
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
-
- dbus_message_iter_close_container(&entry, &array);
- dbus_message_iter_close_container(iter, &entry);
+ struct hdp_feature *feature = data;
+
+ dict_append_entry(dict, "dtype", DBUS_TYPE_UINT16, &feature->dtype);
+ if (feature->dscr)
+ dict_append_entry(dict, "description", DBUS_TYPE_STRING,
+ &feature->dscr);
}

-static void append_array_entry(DBusMessageIter *iter, uint32_t *handler,
- uint8_t *spec, GSList *end_points)
+static void fill_up_specs(DBusMessageIter *dict, gpointer data)
{
- DBusMessageIter dict;
+ GSList *specs = data;
+ GSList *l;
+ struct hdp_feature *feature;
+

- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+ for (l = specs; l; l = l->next) {
+ feature = l->data;

- dict_append_entry(&dict, "id", DBUS_TYPE_UINT32, handler);
- dict_append_entry(&dict, "data_spec", DBUS_TYPE_BYTE, spec);
- append_dict_features(&dict, end_points);
+ hdp_append_dict(dict, fill_up_one_spec, feature);
+ }
+}
+
+static void fill_up_specs_array(DBusMessageIter *array, gpointer data)
+{
+ hdp_append_array_of_dicts(array, fill_up_specs, data);
+}

- dbus_message_iter_close_container(iter, &dict);
+static void fill_up_one_end_point(DBusMessageIter *dict, gpointer data)
+{
+ struct hdp_supp_fts *fts = data;
+ const char *role;
+
+ if (fts->role == HDP_SOURCE)
+ role = HDP_SOURCE_ROLE_AS_STRING;
+ else if (fts->role == HDP_SINK)
+ role = HDP_SINK_ROLE_AS_STRING;
+
+ dict_append_entry(dict, "mdepid", DBUS_TYPE_BYTE, &fts->mdepid);
+ dict_append_entry(dict, "role", DBUS_TYPE_STRING, &role);
+ hdp_append_variant_array_entry(dict, "specs", fill_up_specs_array,
+ fts->features);
+}
+
+static void fill_up_end_points(DBusMessageIter *dict, gpointer data)
+{
+ GSList *end_points = data;
+ struct hdp_supp_fts *fts;
+ GSList *l;
+
+ for (l = end_points; l; l = l->next) {
+ fts = l->data;
+
+ if (fts->role != HDP_SOURCE && fts->role != HDP_SINK)
+ continue;
+
+ hdp_append_dict(dict, fill_up_one_end_point, fts);
+ }
+}
+
+static void fill_up_end_points_array(DBusMessageIter *iter, gpointer data)
+{
+ hdp_append_array_of_dicts(iter, fill_up_end_points, data);
+}
+
+static void fill_up_instance(DBusMessageIter *entry, gpointer data)
+{
+ struct health_instances_aux *aux = data;
+ guint32 handler = aux->handler;
+ guint8 data_spec = aux->data_spec;
+ GSList *end_points = aux->end_points;
+
+ dict_append_entry(entry, "id", DBUS_TYPE_UINT32, &handler);
+ dict_append_entry(entry, "data_spec", DBUS_TYPE_BYTE, &data_spec);
+ hdp_append_variant_array_entry(entry, "end_points",
+ fill_up_end_points_array, end_points);
+}
+
+static void fill_up_health_instances(DBusMessageIter *dict, gpointer data)
+{
+ sdp_list_t *recs = data;
+ sdp_record_t *rec;
+ sdp_list_t *l;
+ guint8 data_spec;
+ GSList *end_points;
+ struct health_instances_aux *aux;
+
+ for (l = recs; l; l = l->next) {
+ rec = l->data;
+ DBG("Record found 0x%x", rec->handle);
+
+ if (!hdp_get_data_exchange_spec(rec, &data_spec)) {
+ error("Error getting data exchange info");
+ continue;
+ }
+ end_points = hdp_get_end_points(rec);
+ if (!end_points) {
+ error("Error getting end points");
+ continue;
+ }
+ aux = g_new0(struct health_instances_aux, 1);
+ aux->handler = rec->handle;
+ aux->data_spec = data_spec;
+ aux->end_points = end_points;
+ hdp_append_dict(dict, fill_up_instance, aux);
+ g_free(aux);
+ }
}

static void sink_health_instances(sdp_list_t *recs, int err, gpointer user_data)
@@ -129,11 +208,7 @@ static void sink_health_instances(sdp_list_t *recs, int err, gpointer user_data)
DBusMessage *msg = cb_data->msg;
struct hdp_device *device = cb_data->device;
DBusMessage *reply;
- sdp_record_t *rec;
- sdp_list_t *l;
- guint8 data_spec;
- GSList *end_points;
- DBusMessageIter iter, dict;
+ DBusMessageIter iter;

g_free(cb_data);

@@ -151,34 +226,8 @@ static void sink_health_instances(sdp_list_t *recs, int err, gpointer user_data)

dbus_message_iter_init_append(reply, &iter);

- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_ARRAY_AS_STRING
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
- for (l = recs; l; l = l->next) {
- rec = l->data;
- DBG("Record found 0x%x", rec->handle);
+ hdp_append_array_of_dicts(&iter, fill_up_health_instances, recs);

- if (!hdp_get_data_exchange_spec(rec, &data_spec)) {
- error("Error getting data exchange info");
- continue;
- }
- end_points = hdp_get_end_points(rec);
- if (!end_points) {
- error("Error getting end points");
- continue;
- }
- append_array_entry(&dict, &rec->handle, &data_spec,
- end_points);
- }
-
- dbus_message_iter_close_container(&iter, &dict);
-/*
- reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
- "Not implemented yet");
-*/
g_dbus_send_message(device->conn, reply);
}

diff --git a/health/hdp_util.c b/health/hdp_util.c
index 2f09417..921a1e1 100644
--- a/health/hdp_util.c
+++ b/health/hdp_util.c
@@ -328,9 +328,9 @@ static gboolean parse_role(DBusMessageIter *iter, GError **err, gpointer data)
}

dbus_message_iter_get_basic(string, &role);
- if (g_strcmp0(role, "sink") == 0)
+ if (g_strcmp0(role, HDP_SINK_ROLE_AS_STRING) == 0)
fts->role = HDP_SINK;
- else if (g_strcmp0(role, "source") == 0)
+ else if (g_strcmp0(role, HDP_SOURCE_ROLE_AS_STRING) == 0)
fts->role = HDP_SOURCE;
else {
g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
@@ -949,3 +949,55 @@ GSList *hdp_get_end_points(const sdp_record_t *rec)

return epl;
}
+
+void hdp_append_array_of_dicts(DBusMessageIter *iter, hdp_dbus_fill_up fill_up,
+ gpointer user_data)
+{
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ fill_up(&dict, user_data);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+void hdp_append_dict(DBusMessageIter *iter, hdp_dbus_fill_up fill_up,
+ gpointer user_data)
+{
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &entry);
+
+ fill_up(&entry, user_data);
+
+ dbus_message_iter_close_container(iter, &entry);
+}
+
+void hdp_append_variant_array_entry(DBusMessageIter *iter, char *key,
+ hdp_dbus_fill_up fill_up, gpointer user_data)
+{
+ DBusMessageIter entry, array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
+
+ fill_up(&array, user_data);
+
+ dbus_message_iter_close_container(&entry, &array);
+ dbus_message_iter_close_container(iter, &entry);
+}
diff --git a/health/hdp_util.h b/health/hdp_util.h
index beafa00..08e6471 100644
--- a/health/hdp_util.h
+++ b/health/hdp_util.h
@@ -29,10 +29,22 @@
#include <gdbus.h>
#include "hdp_types.h"

+#define HDP_SINK_ROLE_AS_STRING "sink"
+#define HDP_SOURCE_ROLE_AS_STRING "source"
+
+typedef void (*hdp_dbus_fill_up)(DBusMessageIter *iter, gpointer data);
+
struct hdp_config *hdp_get_config(DBusMessageIter *iter, GError **err);
gboolean hdp_register_sdp_record(struct hdp_instance *hdps);
gboolean hdp_get_data_exchange_spec(const sdp_record_t *rec, guint8 *val);
GSList *hdp_get_end_points(const sdp_record_t *rec);
void hdp_instance_free(struct hdp_instance *hdpi);

+void hdp_append_array_of_dicts(DBusMessageIter *iter, hdp_dbus_fill_up fill_up,
+ gpointer user_data);
+void hdp_append_dict(DBusMessageIter *iter, hdp_dbus_fill_up fil_up,
+ gpointer user_data);
+void hdp_append_variant_array_entry(DBusMessageIter *iter, char *key,
+ hdp_dbus_fill_up fill_up, gpointer user_data);
+
#endif /* __HDP_UTIL_H__ */
--
1.6.3.3


Subject: [PATCH 15/32] Implement connect MCL callback in health instances connection

---
health/hdp.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 73 insertions(+), 3 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index e57d64c..f5dc107 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -33,6 +33,8 @@

#include "glib-helper.h"

+#include "../mcap/mcap.h"
+
#include "../src/dbus-common.h"

#define HEALTH_MANAGER_INTERFACE "org.bluez.HealthAdapter"
@@ -305,12 +307,77 @@ static sdp_record_t *get_record(sdp_list_t *recs, uint32_t handle)
return NULL;
}

+static void hdp_mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data)
+{
+ /* struct hdp_link *hdpl = data; */
+ DBG("TODO: Incomplete callback, mdl connected");
+}
+
+static void hdp_mcap_mdl_deleted_cb(struct mcap_mdl *mdl, void *data)
+{
+ /* struct hdp_link *hdpl = data; */
+ DBG("TODO: Incomplete callback, mdl deleted");
+}
+
+static void hdp_mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
+{
+ /* struct hdp_link *hdpl = data; */
+ DBG("TODO: Incomplete callback, mdl aborted");
+}
+
+static void hdp_mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data)
+{
+ /* struct hdp_link *hdpl = data; */
+ DBG("TODO: Incomplete callback, mdl closed");
+}
+
+static uint8_t hdp_mcap_mdl_conn_req_cb(struct mcap_mdl *mdl, void *data)
+{
+ /* struct hdp_link *hdpl = data; */
+ DBG("TODO: Incomplete callback, mdl connection request");
+ return MCAP_REQUEST_NOT_SUPPORTED;
+}
+
+static uint8_t hdp_mcap_mdl_reconn_req_cb(struct mcap_mcl *mcl,
+ uint8_t mdepid, uint16_t mdlid,
+ uint8_t *conf, void *data)
+{
+ DBG("TODO: Incomplete callback, mdl reconnection request");
+ return MCAP_REQUEST_NOT_SUPPORTED;
+}
+
static void hdp_mcl_connect_cb(struct mcap_mcl *mcl, GError *err, void *data)
{
struct hdp_connection_cb *cb_data = data;
+ struct hdp_device *device = cb_data->device;
+ /* struct hdp_instance *hdpi = cb_data->hdpi; */
+ DBusMessage *msg = cb_data->msg;
+ GError *cberr = NULL;
+ DBusMessage *reply;

- /* TODO */
g_free(cb_data);
+
+ if (err)
+ goto fail;
+
+ /* Create and Register HealthLink interface */
+ mcap_mcl_set_cb(mcl, &cberr, NULL /*health_link*/,
+ MCAP_MDL_CB_CONNECTED, hdp_mcap_mdl_connected_cb,
+ MCAP_MDL_CB_CLOSED, hdp_mcap_mdl_closed_cb,
+ MCAP_MDL_CB_DELETED, hdp_mcap_mdl_deleted_cb,
+ MCAP_MDL_CB_ABORTED, hdp_mcap_mdl_aborted_cb,
+ MCAP_MDL_CB_REMOTE_CONN_REQ, hdp_mcap_mdl_conn_req_cb,
+ MCAP_MDL_CB_REMOTE_RECONN_REQ, hdp_mcap_mdl_reconn_req_cb,
+ MCAP_MDL_CB_INVALID);
+ if (cberr)
+ goto fail;
+ return;
+fail:
+ reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HdpError",
+ (err ? err->message : cberr->message));
+ if (!cberr)
+ g_error_free(cberr);
+ g_dbus_send_message(device->conn, reply);
}

static void connect_health_instance(sdp_list_t *recs, int err, gpointer data)
@@ -383,7 +450,7 @@ static DBusMessage *hdp_connect(DBusConnection *conn,
if (!l)
return g_dbus_create_error(msg,
ERROR_INTERFACE ".InvalidArguments",
- "Invalid local instance id");
+ "Invalid local health instance id");

cb_data = g_new0(struct hdp_connection_cb, 1);
cb_data->device = device;
@@ -400,6 +467,7 @@ static DBusMessage *hdp_connect(DBusConnection *conn,
cb_data, NULL) == 0)
return NULL;

+ g_free(cb_data);
return g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
"Error getting remote information");
}
@@ -499,6 +567,7 @@ static DBusMessage *hdp_create_instance(DBusConnection *conn,
return g_dbus_create_error(msg,
ERROR_INTERFACE ".InvalidArguments",
"Invalid arguments in method call");
+
dbus_message_iter_get_basic(&iter, &path);
dbus_message_iter_next(&iter);
config = hdp_get_config(&iter, &err);
@@ -509,6 +578,7 @@ static DBusMessage *hdp_create_instance(DBusConnection *conn,
g_error_free(err);
return reply;
}
+
name = dbus_message_get_sender(msg);
if (!name) {
return g_dbus_create_error(msg,
@@ -552,7 +622,7 @@ static DBusMessage *hdp_create_instance(DBusConnection *conn,
DBUS_TYPE_INVALID);
error:
reply = g_dbus_create_error(msg,ERROR_INTERFACE ".HealthError",
- err->message);
+ err->message);
g_error_free(err);
hdp_instance_free(hdpi);
return reply;
--
1.6.3.3


Subject: [PATCH 13/32] Implement connection of health instances

---
health/hdp.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++---
health/hdp_util.c | 26 ++++++++++++++
health/hdp_util.h | 2 +
3 files changed, 123 insertions(+), 5 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 840af0f..b9f637e 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -52,6 +52,13 @@ struct health_instances_aux {
GSList *end_points;
};

+struct hdp_connection_cb {
+ struct hdp_device *device;
+ struct hdp_instance *hdpi;
+ uint32_t rem_id;
+ DBusMessage *msg;
+};
+
static struct hdp_adapter *find_adapter(GSList *list,
struct btd_adapter *btd_adapter)
{
@@ -240,7 +247,7 @@ static DBusMessage *get_health_instances(DBusConnection *conn,
bdaddr_t src, dst;
uuid_t uuid;

- adapter = device_get_adapter(device->dev);
+ adapter = device->hdp_adapter->btd_adapter;
adapter_get_address(adapter, &src);
device_get_address(device->dev, &dst);

@@ -284,12 +291,82 @@ static void dev_path_unregister(void *data)
health_device_free(device);
}

+static sdp_record_t *get_record(sdp_list_t *recs, uint32_t handle)
+{
+ sdp_record_t *rec;
+ sdp_list_t *l;
+
+ for (l = recs; l; l = l->next) {
+ rec = l->data;
+ if (rec->handle == handle)
+ return rec;
+ }
+
+ return NULL;
+}
+
+static void hdp_mcl_connect_cb(struct mcap_mcl *mcl, GError *err, void *data)
+{
+ struct hdp_connection_cb *cb_data = data;
+
+ /* TODO */
+ g_free(cb_data);
+}
+
+static void connect_health_instance(sdp_list_t *recs, int err, gpointer data)
+{
+ struct hdp_connection_cb *cb_data = data;
+ struct hdp_device *device = cb_data->device;
+ struct hdp_instance *hdpi = cb_data->hdpi;
+ GError *gerr = NULL;
+ uint32_t rid = cb_data->rem_id;
+ DBusMessage *msg = cb_data->msg;
+ DBusMessage *reply;
+ sdp_record_t *rec;
+ guint16 ccpsm, version;
+ bdaddr_t dst;
+
+ if (err != 0) {
+ reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "Error getting remote information");
+ goto fail;
+ }
+
+ rec = get_record(recs, rid);
+ if (!rec) {
+ reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "Error getting remote information");
+ goto fail;
+ }
+
+ if (!hdp_get_prot_desc_list(rec, &ccpsm, &version)) {
+ reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "Error getting remote protocol descriptor list");
+ goto fail;
+ }
+
+ device_get_address(device->dev, &dst);
+ mcap_create_mcl(hdpi->mi, &dst, ccpsm, &gerr, hdp_mcl_connect_cb,
+ cb_data);
+ if (!gerr)
+ return;
+
+ reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "Error getting remote protocol descriptor list");
+fail:
+ g_dbus_send_message(device->conn, reply);
+ g_free(cb_data);
+}
+
static DBusMessage *hdp_connect(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
struct hdp_device *device = user_data;
- struct hdp_instance *hdpi;
+ struct hdp_connection_cb *cb_data;
+ struct btd_adapter *adapter;
+ bdaddr_t src, dst;
uint32_t lid, rid;
+ uuid_t uuid;
GSList *l;

if (!dbus_message_get_args(msg, NULL,
@@ -307,11 +384,24 @@ static DBusMessage *hdp_connect(DBusConnection *conn,
return g_dbus_create_error(msg,
ERROR_INTERFACE ".InvalidArguments",
"Invalid local instance id");
- hdpi = l->data;

+ cb_data = g_new0(struct hdp_connection_cb, 1);
+ cb_data->device = device;
+ cb_data->hdpi = l->data;
+ cb_data->rem_id = rid;
+ cb_data->msg = dbus_message_ref(msg);

- return g_dbus_create_error(msg, ERROR_INTERFACE ".HdpError",
- "Function is not yet implemented");
+ adapter = device->hdp_adapter->btd_adapter;
+ adapter_get_address(adapter, &src);
+ device_get_address(device->dev, &dst);
+
+ bt_string2uuid(&uuid, HDP_UUID);
+ if (bt_search_service(&src, &dst, &uuid, connect_health_instance,
+ cb_data, NULL) == 0)
+ return NULL;
+
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "Error getting remote information");
}

static GDBusMethodTable device_methods[] = {
diff --git a/health/hdp_util.c b/health/hdp_util.c
index 921a1e1..066147a 100644
--- a/health/hdp_util.c
+++ b/health/hdp_util.c
@@ -883,6 +883,32 @@ gboolean hdp_get_data_exchange_spec(const sdp_record_t *rec, guint8 *val)
return TRUE;
}

+gboolean hdp_get_prot_desc_list(const sdp_record_t *rec, guint16 *psm,
+ guint16 *version)
+{
+ if (!(psm || version))
+ return TRUE;
+
+ /* TODO:
+ sdp_data_t *pdl, *l;
+
+ exspec = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
+ if (exspec->dtd != SDP_SEQ8)
+ return FALSE;
+
+ for (l = pdl->val.dataseq; l; l = l->next) {
+ if (l->dtd != SDP_SEQ8)
+ continue;
+ epl = get_feature(epl, l->val.dataseq);
+ }
+ */
+ if (psm)
+ *psm = 0x1001;
+ if (version)
+ *version = 0x0100;
+ return TRUE;
+}
+
static gint cmp_feat_mdep(gconstpointer a, gconstpointer b)
{
const struct hdp_supp_fts *fts = a;
diff --git a/health/hdp_util.h b/health/hdp_util.h
index 08e6471..03a7256 100644
--- a/health/hdp_util.h
+++ b/health/hdp_util.h
@@ -37,6 +37,8 @@ typedef void (*hdp_dbus_fill_up)(DBusMessageIter *iter, gpointer data);
struct hdp_config *hdp_get_config(DBusMessageIter *iter, GError **err);
gboolean hdp_register_sdp_record(struct hdp_instance *hdps);
gboolean hdp_get_data_exchange_spec(const sdp_record_t *rec, guint8 *val);
+gboolean hdp_get_prot_desc_list(const sdp_record_t *rec, guint16 *psm,
+ guint16 *version);
GSList *hdp_get_end_points(const sdp_record_t *rec);
void hdp_instance_free(struct hdp_instance *hdpi);

--
1.6.3.3


Subject: [PATCH 14/32] Manage mcap instances

---
health/hdp.c | 73 ++++++++++++++++++++++++++++++++++++++++------------
health/hdp_util.c | 3 +-
2 files changed, 58 insertions(+), 18 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index b9f637e..e57d64c 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -457,6 +457,30 @@ static void client_disconnected(DBusConnection *connection, void *user_data)
hdp_instance_free(hdpi);
}

+static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
+{
+ /* struct hdp_instance *hdpi = data; */
+ DBG("TODO: implement mcl_connected");
+}
+
+static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
+{
+ /* struct hdp_instance *hdpi = data; */
+ DBG("TODO: implement mcl_reconnected");
+}
+
+static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
+{
+ /* struct hdp_instance *hdpi = data; */
+ DBG("TODO: implement mcl_disconnected");
+}
+
+static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
+{
+ /* struct hdp_instance *hdpi = data; */
+ DBG("TODO: implement mcl_uncached");
+}
+
static DBusMessage *hdp_create_instance(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
@@ -472,17 +496,24 @@ static DBusMessage *hdp_create_instance(DBusConnection *conn,
dbus_message_iter_init(msg, &iter);
ctype = dbus_message_iter_get_arg_type(&iter);
if (ctype != DBUS_TYPE_OBJECT_PATH)
- goto error;
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
dbus_message_iter_get_basic(&iter, &path);
dbus_message_iter_next(&iter);
config = hdp_get_config(&iter, &err);
- if (err)
- goto error;
+ if (err) {
+ reply = g_dbus_create_error(msg,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments: %s", err->message);
+ g_error_free(err);
+ return reply;
+ }
name = dbus_message_get_sender(msg);
if (!name) {
- g_set_error(&err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
- "Can't get sender name");
- goto error;
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Can't get sender name");
}

hdpi = g_new0(struct hdp_instance, 1);
@@ -494,7 +525,20 @@ static DBusMessage *hdp_create_instance(DBusConnection *conn,
hdpi->dbus_watcher = g_dbus_add_disconnect_watch(adapter->conn, name,
client_disconnected, hdpi, NULL);

- /* TODO: Create mcap instance */
+ hdpi->mi = mcap_create_instance(adapter->btd_adapter, BT_IO_SEC_MEDIUM,
+ 0, 0, &err, mcl_connected,
+ mcl_reconnected, mcl_disconnected,
+ mcl_uncached, hdpi);
+ if (err)
+ goto error;
+
+ hdpi->ccpsm = mcap_get_ctrl_psm(hdpi->mi, &err);
+ if (err)
+ goto error;
+
+ hdpi->dcpsm = mcap_get_data_psm(hdpi->mi, &err);
+ if (err)
+ goto error;

if (!hdp_register_sdp_record(hdpi)) {
hdp_instance_free(hdpi);
@@ -503,19 +547,14 @@ static DBusMessage *hdp_create_instance(DBusConnection *conn,
}

adapter->instances = g_slist_prepend(adapter->instances, hdpi);
- info("HDP instance created with path %d", hdpi->id);
+ DBG("HDP instance created with id %d", hdpi->id);
return g_dbus_create_reply(msg, DBUS_TYPE_UINT32, &hdpi->id,
DBUS_TYPE_INVALID);
error:
- if (err) {
- reply = g_dbus_create_error(msg,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments: %s", err->message);
- g_error_free(err);
- } else
- reply = g_dbus_create_error(msg,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments in method call");
+ reply = g_dbus_create_error(msg,ERROR_INTERFACE ".HealthError",
+ err->message);
+ g_error_free(err);
+ hdp_instance_free(hdpi);
return reply;
}

diff --git a/health/hdp_util.c b/health/hdp_util.c
index 066147a..a7fc1d5 100644
--- a/health/hdp_util.c
+++ b/health/hdp_util.c
@@ -123,7 +123,8 @@ void hdp_instance_free(struct hdp_instance *hdpi)
g_dbus_remove_watch(hdpi->adapter->conn, hdpi->dbus_watcher);
if (hdpi->sdp_handler)
remove_record_from_server(hdpi->sdp_handler);
- /* TODO: stop mcap instance */
+ if (hdpi->mi)
+ mcap_release_instance(hdpi->mi);
if (hdpi->apath)
g_free(hdpi->apath);
if (hdpi->aname)
--
1.6.3.3


Subject: [PATCH 10/32] Insert end_point in array returned by get_health_instances

---
health/hdp.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++--
health/hdp_util.c | 2 -
2 files changed, 57 insertions(+), 5 deletions(-)

diff --git a/health/hdp.c b/health/hdp.c
index 4fb9d62..8690f0e 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -76,6 +76,43 @@ static struct hdp_device *find_device(GSList *devices, struct btd_device *dev)
return NULL;
}

+static void append_dict_features(DBusMessageIter *iter, GSList *end_points)
+{
+ DBusMessageIter entry, array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, "end_points");
+
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
+
+ dbus_message_iter_close_container(&entry, &array);
+ dbus_message_iter_close_container(iter, &entry);
+}
+
+static void append_array_entry(DBusMessageIter *iter, uint32_t *handler,
+ uint8_t *spec, GSList *end_points)
+{
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ dict_append_entry(&dict, "id", DBUS_TYPE_UINT32, handler);
+ dict_append_entry(&dict, "data_spec", DBUS_TYPE_BYTE, spec);
+ append_dict_features(&dict, end_points);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
static void sink_health_instances(sdp_list_t *recs, int err, gpointer user_data)
{
struct instances_aux *cb_data = user_data;
@@ -86,6 +123,7 @@ static void sink_health_instances(sdp_list_t *recs, int err, gpointer user_data)
sdp_list_t *l;
guint8 data_spec;
GSList *end_points;
+ DBusMessageIter iter, dict;

g_free(cb_data);

@@ -97,10 +135,22 @@ static void sink_health_instances(sdp_list_t *recs, int err, gpointer user_data)
return;
}

+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
for (l = recs; l; l = l->next) {
rec = l->data;
DBG("Record found 0x%x", rec->handle);
- /* TODO: Check record */
+
if (!hdp_get_data_exchange_spec(rec, &data_spec)) {
error("Error getting data exchange info");
continue;
@@ -110,11 +160,15 @@ static void sink_health_instances(sdp_list_t *recs, int err, gpointer user_data)
error("Error getting end points");
continue;
}
- DBG("Get data exchange spec %d", data_spec);
+ append_array_entry(&dict, &rec->handle, &data_spec,
+ end_points);
}

+ dbus_message_iter_close_container(&iter, &dict);
+/*
reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
"Not implemented yet");
+*/
g_dbus_send_message(device->conn, reply);
}

@@ -172,7 +226,7 @@ static void dev_path_unregister(void *data)
}

static GDBusMethodTable device_methods[] = {
- { "GetHealthInstances", "", "a{sv}", get_health_instances,
+ { "GetHealthInstances", "", "aa{sv}", get_health_instances,
G_DBUS_METHOD_FLAG_ASYNC },
{ NULL }
};
diff --git a/health/hdp_util.c b/health/hdp_util.c
index 0b39151..2f09417 100644
--- a/health/hdp_util.c
+++ b/health/hdp_util.c
@@ -947,7 +947,5 @@ GSList *hdp_get_end_points(const sdp_record_t *rec)
epl = get_feature(epl, l->val.dataseq);
}

- g_slist_foreach(epl, print_features, NULL);
-
return epl;
}
--
1.6.3.3


Subject: [PATCH 09/32] Adds functions to get remote suported features from its SDP record

---
health/hdp_util.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 69 insertions(+), 5 deletions(-)

diff --git a/health/hdp_util.c b/health/hdp_util.c
index 731154b..0b39151 100644
--- a/health/hdp_util.c
+++ b/health/hdp_util.c
@@ -781,11 +781,6 @@ static gboolean register_data_exchange_spec(struct hdp_config *config,
return TRUE;
}

-GSList *hdp_get_end_points(const sdp_record_t *rec)
-{
- return NULL;
-}
-
static gboolean register_mcap_features(sdp_record_t *sdp_record)
{
sdp_data_t *mcap_proc;
@@ -887,3 +882,72 @@ gboolean hdp_get_data_exchange_spec(const sdp_record_t *rec, guint8 *val)
*val = exspec->val.uint8;
return TRUE;
}
+
+static gint cmp_feat_mdep(gconstpointer a, gconstpointer b)
+{
+ const struct hdp_supp_fts *fts = a;
+ const guint8 *mdep = b;
+
+ if (fts->mdepid == *mdep)
+ return 0;
+ return -1;
+}
+
+static GSList *get_feature(GSList *epl, sdp_data_t *feat_seq)
+{
+ struct hdp_supp_fts *fts;
+ struct hdp_feature *feat;
+ GSList *l;
+ sdp_data_t *mdepid, *dtype, *role, *desc;
+
+ mdepid = feat_seq;
+ if (!mdepid || mdepid->dtd != SDP_UINT8)
+ return epl;
+ dtype = mdepid->next;
+ if (!dtype || dtype->dtd != SDP_UINT16)
+ return epl;
+ role = dtype->next;
+ if (!role || role->dtd != SDP_UINT8)
+ return epl;
+ desc = role->next;
+
+ l = g_slist_find_custom(epl, &mdepid->val.uint8, cmp_feat_mdep);
+ if (l) {
+ fts = l->data;
+ if (fts->role != role->val.uint8)
+ return epl;
+ } else {
+ fts = g_new0(struct hdp_supp_fts, 1);
+ fts->mdepid = mdepid->val.uint8;
+ fts->role = role->val.uint8;
+ epl = g_slist_prepend(epl, fts);
+ }
+
+ feat = g_new0(struct hdp_feature, 1);
+ feat->dtype = dtype->val.uint16;
+ if (desc && desc->dtd == SDP_TEXT_STR8)
+ feat->dscr = g_strdup(desc->val.str);
+ fts->features = g_slist_prepend(fts->features, feat);
+ return epl;
+}
+
+GSList *hdp_get_end_points(const sdp_record_t *rec)
+{
+ GSList *epl = NULL;
+ sdp_data_t *end_points, *l;
+
+ end_points = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST);
+
+ if (end_points->dtd != SDP_SEQ8)
+ return NULL;
+
+ for (l = end_points->val.dataseq; l; l = l->next) {
+ if (l->dtd != SDP_SEQ8)
+ continue;
+ epl = get_feature(epl, l->val.dataseq);
+ }
+
+ g_slist_foreach(epl, print_features, NULL);
+
+ return epl;
+}
--
1.6.3.3


Subject: [PATCH 01/32] Add Health api description

This API describes the interface shown by the Health plugin through d-bus
---
doc/health-api.txt | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 217 insertions(+), 0 deletions(-)
create mode 100644 doc/health-api.txt

diff --git a/doc/health-api.txt b/doc/health-api.txt
new file mode 100644
index 0000000..83e29af
--- /dev/null
+++ b/doc/health-api.txt
@@ -0,0 +1,217 @@
+BlueZ D-Bus Health API description
+**********************************
+
+ Santiago Carot-Nemesio <[email protected]>
+ José Antonio Santos-Cadenas <[email protected]>
+ Elvis Pfützenreuter <[email protected]>
+
+Health Device Profile hierarchy
+===============================
+
+Service org.bluez
+Interface org.bluez.HealthAdapter
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods:
+
+ uint32 CreateInstance(object path, dict config)
+
+ Returns the id of the new created instance. The path parameter
+ is the path of the remote object with the callbacks to notify
+ events (see org.bluez.HealthAgent at the end of this document)
+ This petition starts an mcap instance and also register in the
+ SDP if is needed.
+
+ Dict is defined as bellow:
+ { "data_spec" : The data_spec is the data exchange specification
+ (see section 5.2.10 of the specification
+ document) possible values:
+ 0x00 = reserved,
+ 0x01 [IEEE 11073-20601],
+ 0x02..0xff reserved,
+ (optional)
+ "end_points" : [{ (optional)
+ "mdepid" : uint8, (optional)
+ "role" : ("source" or "sink"), (mandatory)
+ "specs" :[{ (mandatory)
+ "data_type" : uint16, (mandatory)
+ "description" : string, (optional)
+ }]
+ }]
+ }
+
+ if "data_spec" is not set, no SDP record will be registered, so
+ all the other data in the dictionary will be ignored.
+
+ Instance will be closed by the call or implicitly when the
+ programs leaves the bus.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+
+ void CloseInstance(uint32 )
+
+ Closes the HDP instance identified by the object path. Also
+ instance will be closed if the process that started leaves the
+ bus. If there is a SDP record associated to this instance it
+ will be removed.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotFound
+
+--------------------------------------------------------------------------------
+
+Service org.bluez
+Interface org.bluez.HealthDevice
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods:
+
+ array GetHealthInstances()
+
+ Gets the information of the remote instances present in this
+ device and published on its SDP record. The returned data
+ follows this format.
+
+ [{"id": uint32,
+ "data_spec" : data spec,
+ "end_points":
+ ["mdepid": uint8,
+ "role" : "source" or "sink" ,
+ "specs" : [{
+ "dtype" : uint16,
+ "description" : string, (optional)
+ }]
+ ]
+ }];
+
+ object Connect(uint32 local_instance_id, uint32 remote_instance_id)
+
+ Connects the local instance with the remote instance and returns
+ the path of the HealthLink object. You should get the remote
+ instance id running GetHealthInstances.
+
+ Only the bus client that created the local session will be able
+ to create connections using it.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.HealthError
+
+ void Disconnect(object link, boolean cache)
+
+ Disconnect from the link. If cache is false, state will also be
+ deleted. Otherwise, the state will be kept for allowing future
+ reconnections until the adapter holding the local session is
+ removed.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotFound
+ org.bluez.Error.HealthError
+
+--------------------------------------------------------------------------------
+
+Service org.bluez
+Interface org.bluez.HealthLink
+Object path [variable prefix]/{hci0,hci1,...}/{hdp0,hdp1,...}/rem_inst_id
+
+Methods:
+
+ boolean Echo(array{byte})
+
+ Sends an echo petition to the remote intance. Returns True if
+ response matches with the buffer sent. If some error is detected
+ False value is returned and the associated MCL is closed.
+
+ uint16 OpenDataChannel(byte mdepid, byte config)
+
+ Creates a new data channel with the indicated config to the
+ remote MCAP Data End Point (MDEP).
+ The configuration should indicate the channel quality of
+ service. In the current version of HDP, valid values are 0x01
+ for reliable channels and 0x02 for streaming data channel.
+
+ Returns the data channel id.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.HealthError
+
+ array GetDataChannelFileDescriptor(uint16 mdlid)
+
+ Gets a file descriptor where data can be read or
+ written for receive or sent by the data channel.
+ Returns an array of file descriptors one for write
+ and other for read.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotFound
+ org.bluez.Error.HealthError
+
+ void DeleteDataChannel(uint16 mdlid)
+
+ Deletes a data channel so it will not be available for
+ use.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotFound
+ org.bluez.Error.HealthError
+
+ void DeleteAllDataChannels()
+
+ Deletes all data channels so it will not be available
+ for use. Typically this function is called when the
+ connection with the remote device will be closed
+ permanently
+
+ Possible errors: org.bluez.Error.HealthError
+
+ dict GetDataChannelStatus()
+
+ Return a dictionary with all the data channels that
+ can be used to send data right now. The dictionary
+ is formed like follows:
+ {
+ "reliable": [mdlid_r1, mdlid_r2, ...],
+ "streaming" : [mdlid_s1, mdlid_s2, ...]
+ }
+
+ The fist reliable data channel will always be the first
+ data channel in reliable array.
+
+HealthAgent hierarchy
+=====================
+
+(this object is implemented by the HDP user in order to receive notifications)
+
+Service unique name
+Interface org.bluez.HealthAgent
+Object path freely definable
+
+Methods:
+
+ void LinkConnected(object path)
+
+ This method is called whenever a new connection has been
+ established over the control channel of the current HDP
+ instance. The object path paremeter contains the object path of
+ the created HealthLink.
+
+ void LinkDisconnected(object path)
+
+ This method is called when a remote device is disconnected
+ definitively. Any future reconnections will fail. Also all data
+ channels associated to this device will be closed.
+
+ void CreatedDataChannel(object path, uint16 mdlid, byte conf)
+
+ This method is called when a new data channel is created
+
+ The path contains the object path of the HealthLink where the
+ new connection is created, the mdlid is the data channel
+ identificator and conf is the que quality of service of the data
+ channel (0x01 reliable, 0x02 streaming).
+
+ void DeletedDataChannel(object path, uint16 mdlid)
+
+ This method is called when a data channel is closed.
+
+ After this call the data channel id will not be valid and can be
+ reused for future created data channels.
--
1.6.3.3