Blame profiles/health/hdp.c

Packit 34410b
/*
Packit 34410b
 *
Packit 34410b
 *  BlueZ - Bluetooth protocol stack for Linux
Packit 34410b
 *
Packit 34410b
 *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
Packit 34410b
 *
Packit 34410b
 *  This program is free software; you can redistribute it and/or modify
Packit 34410b
 *  it under the terms of the GNU General Public License as published by
Packit 34410b
 *  the Free Software Foundation; either version 2 of the License, or
Packit 34410b
 *  (at your option) any later version.
Packit 34410b
 *
Packit 34410b
 *  This program is distributed in the hope that it will be useful,
Packit 34410b
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 34410b
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 34410b
 *  GNU General Public License for more details.
Packit 34410b
 *
Packit 34410b
 *  You should have received a copy of the GNU General Public License
Packit 34410b
 *  along with this program; if not, write to the Free Software
Packit 34410b
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Packit 34410b
 *
Packit 34410b
 */
Packit 34410b
Packit 34410b
#ifdef HAVE_CONFIG_H
Packit 34410b
#include <config.h>
Packit 34410b
#endif
Packit 34410b
Packit 34410b
#define _GNU_SOURCE
Packit 34410b
#include <stdlib.h>
Packit 34410b
#include <stdint.h>
Packit 34410b
#include <stdbool.h>
Packit 34410b
#include <unistd.h>
Packit 34410b
Packit 34410b
#include <glib.h>
Packit 34410b
Packit 34410b
#include "lib/bluetooth.h"
Packit 34410b
#include "lib/l2cap.h"
Packit 34410b
#include "lib/sdp.h"
Packit 34410b
Packit 34410b
#include "gdbus/gdbus.h"
Packit 34410b
Packit 34410b
#include "src/dbus-common.h"
Packit 34410b
#include "src/log.h"
Packit 34410b
#include "src/error.h"
Packit 34410b
#include "src/adapter.h"
Packit 34410b
#include "src/device.h"
Packit 34410b
#include "src/sdpd.h"
Packit 34410b
#include "btio/btio.h"
Packit 34410b
Packit 34410b
#include "hdp_types.h"
Packit 34410b
#include "hdp_util.h"
Packit 34410b
#include "hdp.h"
Packit 34410b
#include "mcap.h"
Packit 34410b
Packit 34410b
#define ECHO_TIMEOUT	1 /* second */
Packit 34410b
#define HDP_ECHO_LEN	15
Packit 34410b
Packit 34410b
static GSList *applications = NULL;
Packit 34410b
static GSList *devices = NULL;
Packit 34410b
static uint8_t next_app_id = HDP_MDEP_INITIAL;
Packit 34410b
Packit 34410b
static GSList *adapters;
Packit 34410b
Packit 34410b
static struct hdp_device *create_health_device(struct btd_device *device);
Packit 34410b
static void free_echo_data(struct hdp_echo_data *edata);
Packit 34410b
Packit 34410b
struct hdp_create_dc {
Packit 34410b
	DBusMessage			*msg;
Packit 34410b
	struct hdp_application		*app;
Packit 34410b
	struct hdp_device		*dev;
Packit 34410b
	uint8_t				config;
Packit 34410b
	uint8_t				mdep;
Packit 34410b
	guint				ref;
Packit 34410b
	mcap_mdl_operation_cb		cb;
Packit 34410b
};
Packit 34410b
Packit 34410b
struct hdp_tmp_dc_data {
Packit 34410b
	DBusMessage			*msg;
Packit 34410b
	struct hdp_channel		*hdp_chann;
Packit 34410b
	guint				ref;
Packit 34410b
	mcap_mdl_operation_cb		cb;
Packit 34410b
};
Packit 34410b
Packit 34410b
struct hdp_echo_data {
Packit 34410b
	gboolean		echo_done;	/* Is a echo was already done */
Packit 34410b
	gpointer		buf;		/* echo packet sent */
Packit 34410b
	guint			tid;		/* echo timeout */
Packit 34410b
};
Packit 34410b
Packit 34410b
static struct hdp_channel *hdp_channel_ref(struct hdp_channel *chan)
Packit 34410b
{
Packit 34410b
	if (chan == NULL)
Packit 34410b
		return NULL;
Packit 34410b
Packit 34410b
	chan->ref++;
Packit 34410b
Packit 34410b
	DBG("(%p): ref=%d", chan, chan->ref);
Packit 34410b
	return chan;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void free_health_channel(struct hdp_channel *chan)
Packit 34410b
{
Packit 34410b
	if (chan->mdep == HDP_MDEP_ECHO) {
Packit 34410b
		free_echo_data(chan->edata);
Packit 34410b
		chan->edata = NULL;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	mcap_mdl_unref(chan->mdl);
Packit 34410b
	hdp_application_unref(chan->app);
Packit 34410b
	health_device_unref(chan->dev);
Packit 34410b
	g_free(chan->path);
Packit 34410b
	g_free(chan);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_channel_unref(struct hdp_channel *chan)
Packit 34410b
{
Packit 34410b
	if (chan == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	chan->ref --;
Packit 34410b
	DBG("(%p): ref=%d", chan, chan->ref);
Packit 34410b
Packit 34410b
	if (chan->ref > 0)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	free_health_channel(chan);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void free_hdp_create_dc(struct hdp_create_dc *dc_data)
Packit 34410b
{
Packit 34410b
	dbus_message_unref(dc_data->msg);
Packit 34410b
	hdp_application_unref(dc_data->app);
Packit 34410b
	health_device_unref(dc_data->dev);
Packit 34410b
Packit 34410b
	g_free(dc_data);
Packit 34410b
}
Packit 34410b
Packit 34410b
static struct hdp_create_dc *hdp_create_data_ref(struct hdp_create_dc *dc_data)
Packit 34410b
{
Packit 34410b
	dc_data->ref++;
Packit 34410b
Packit 34410b
	DBG("(%p): ref=%d", dc_data, dc_data->ref);
Packit 34410b
Packit 34410b
	return dc_data;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_create_data_unref(struct hdp_create_dc *dc_data)
Packit 34410b
{
Packit 34410b
	dc_data->ref--;
Packit 34410b
Packit 34410b
	DBG("(%p): ref=%d", dc_data, dc_data->ref);
Packit 34410b
Packit 34410b
	if (dc_data->ref > 0)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	free_hdp_create_dc(dc_data);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void free_hdp_conn_dc(struct hdp_tmp_dc_data *data)
Packit 34410b
{
Packit 34410b
	dbus_message_unref(data->msg);
Packit 34410b
	hdp_channel_unref(data->hdp_chann);
Packit 34410b
Packit 34410b
	g_free(data);
Packit 34410b
}
Packit 34410b
Packit 34410b
static struct hdp_tmp_dc_data *hdp_tmp_dc_data_ref(struct hdp_tmp_dc_data *data)
Packit 34410b
{
Packit 34410b
	data->ref++;
Packit 34410b
Packit 34410b
	DBG("hdp_conn_data_ref(%p): ref=%d", data, data->ref);
Packit 34410b
Packit 34410b
	return data;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_tmp_dc_data_unref(struct hdp_tmp_dc_data *data)
Packit 34410b
{
Packit 34410b
	data->ref--;
Packit 34410b
Packit 34410b
	DBG("hdp_conn_data_unref(%p): ref=%d", data, data->ref);
Packit 34410b
Packit 34410b
	if (data->ref > 0)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	free_hdp_conn_dc(data);
Packit 34410b
}
Packit 34410b
Packit 34410b
static int cmp_app_id(gconstpointer a, gconstpointer b)
Packit 34410b
{
Packit 34410b
	const struct hdp_application *app = a;
Packit 34410b
	const uint8_t *id = b;
Packit 34410b
Packit 34410b
	return app->id - *id;
Packit 34410b
}
Packit 34410b
Packit 34410b
static int cmp_adapter(gconstpointer a, gconstpointer b)
Packit 34410b
{
Packit 34410b
	const struct hdp_adapter *hdp_adapter = a;
Packit 34410b
	const struct btd_adapter *adapter = b;
Packit 34410b
Packit 34410b
	if (hdp_adapter->btd_adapter == adapter)
Packit 34410b
		return 0;
Packit 34410b
Packit 34410b
	return -1;
Packit 34410b
}
Packit 34410b
Packit 34410b
static int cmp_device(gconstpointer a, gconstpointer b)
Packit 34410b
{
Packit 34410b
	const struct hdp_device *hdp_device = a;
Packit 34410b
	const struct btd_device *device = b;
Packit 34410b
Packit 34410b
	if (hdp_device->dev == device)
Packit 34410b
		return 0;
Packit 34410b
Packit 34410b
	return -1;
Packit 34410b
}
Packit 34410b
Packit 34410b
static int cmp_dev_addr(gconstpointer a, gconstpointer dst)
Packit 34410b
{
Packit 34410b
	const struct hdp_device *device = a;
Packit 34410b
Packit 34410b
	return bacmp(device_get_address(device->dev), dst);
Packit 34410b
}
Packit 34410b
Packit 34410b
static int cmp_dev_mcl(gconstpointer a, gconstpointer mcl)
Packit 34410b
{
Packit 34410b
	const struct hdp_device *device = a;
Packit 34410b
Packit 34410b
	if (mcl == device->mcl)
Packit 34410b
		return 0;
Packit 34410b
	return -1;
Packit 34410b
}
Packit 34410b
Packit 34410b
static int cmp_chan_mdlid(gconstpointer a, gconstpointer b)
Packit 34410b
{
Packit 34410b
	const struct hdp_channel *chan = a;
Packit 34410b
	const uint16_t *mdlid = b;
Packit 34410b
Packit 34410b
	return chan->mdlid - *mdlid;
Packit 34410b
}
Packit 34410b
Packit 34410b
static int cmp_chan_path(gconstpointer a, gconstpointer b)
Packit 34410b
{
Packit 34410b
	const struct hdp_channel *chan = a;
Packit 34410b
	const char *path = b;
Packit 34410b
Packit 34410b
	return g_ascii_strcasecmp(chan->path, path);
Packit 34410b
}
Packit 34410b
Packit 34410b
static int cmp_chan_mdl(gconstpointer a, gconstpointer mdl)
Packit 34410b
{
Packit 34410b
	const struct hdp_channel *chan = a;
Packit 34410b
Packit 34410b
	if (chan->mdl == mdl)
Packit 34410b
		return 0;
Packit 34410b
	return -1;
Packit 34410b
}
Packit 34410b
Packit 34410b
static uint8_t get_app_id(void)
Packit 34410b
{
Packit 34410b
	uint8_t id = next_app_id;
Packit 34410b
Packit 34410b
	do {
Packit 34410b
		GSList *l = g_slist_find_custom(applications, &id, cmp_app_id);
Packit 34410b
Packit 34410b
		if (l == NULL) {
Packit 34410b
			next_app_id = (id % HDP_MDEP_FINAL) + 1;
Packit 34410b
			return id;
Packit 34410b
		} else
Packit 34410b
			id = (id % HDP_MDEP_FINAL) + 1;
Packit 34410b
	} while (id != next_app_id);
Packit 34410b
Packit 34410b
	/* No more ids available */
Packit 34410b
	return 0;
Packit 34410b
}
Packit 34410b
Packit 34410b
static int cmp_app(gconstpointer a, gconstpointer b)
Packit 34410b
{
Packit 34410b
	const struct hdp_application *app = a;
Packit 34410b
Packit 34410b
	return g_strcmp0(app->path, b);
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean set_app_path(struct hdp_application *app)
Packit 34410b
{
Packit 34410b
	app->id = get_app_id();
Packit 34410b
	if (app->id == 0)
Packit 34410b
		return FALSE;
Packit 34410b
	app->path = g_strdup_printf(MANAGER_PATH "/health_app_%d", app->id);
Packit 34410b
Packit 34410b
	return TRUE;
Packit 34410b
};
Packit 34410b
Packit 34410b
static void device_unref_mcl(struct hdp_device *hdp_device)
Packit 34410b
{
Packit 34410b
	if (hdp_device->mcl == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	mcap_close_mcl(hdp_device->mcl, FALSE);
Packit 34410b
	mcap_mcl_unref(hdp_device->mcl);
Packit 34410b
	hdp_device->mcl = NULL;
Packit 34410b
	hdp_device->mcl_conn = FALSE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void free_health_device(struct hdp_device *device)
Packit 34410b
{
Packit 34410b
	if (device->dev != NULL) {
Packit 34410b
		btd_device_unref(device->dev);
Packit 34410b
		device->dev = NULL;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	device_unref_mcl(device);
Packit 34410b
Packit 34410b
	g_free(device);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void update_adapter_cb(void *data, void *user_data);
Packit 34410b
Packit 34410b
static void remove_application(struct hdp_application *app)
Packit 34410b
{
Packit 34410b
	DBG("Application %s deleted", app->path);
Packit 34410b
	hdp_application_unref(app);
Packit 34410b
Packit 34410b
	g_slist_foreach(adapters, update_adapter_cb, NULL);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void client_disconnected(DBusConnection *conn, void *user_data)
Packit 34410b
{
Packit 34410b
	struct hdp_application *app = user_data;
Packit 34410b
Packit 34410b
	DBG("Client disconnected from the bus, deleting hdp application");
Packit 34410b
	applications = g_slist_remove(applications, app);
Packit 34410b
Packit 34410b
	app->dbus_watcher = 0; /* Watcher shouldn't be freed in this case */
Packit 34410b
	remove_application(app);
Packit 34410b
}
Packit 34410b
Packit 34410b
static DBusMessage *manager_create_application(DBusConnection *conn,
Packit 34410b
					DBusMessage *msg, void *user_data)
Packit 34410b
{
Packit 34410b
	struct hdp_application *app;
Packit 34410b
	const char *name;
Packit 34410b
	DBusMessageIter iter;
Packit 34410b
	GError *err = NULL;
Packit 34410b
Packit 34410b
	dbus_message_iter_init(msg, &iter);
Packit 34410b
	app = hdp_get_app_config(&iter, &err;;
Packit 34410b
	if (err != NULL) {
Packit 34410b
		g_error_free(err);
Packit 34410b
		return btd_error_invalid_args(msg);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	name = dbus_message_get_sender(msg);
Packit 34410b
	if (name == NULL) {
Packit 34410b
		hdp_application_unref(app);
Packit 34410b
		return g_dbus_create_error(msg,
Packit 34410b
					ERROR_INTERFACE ".HealthError",
Packit 34410b
					"Can't get sender name");
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (!set_app_path(app)) {
Packit 34410b
		hdp_application_unref(app);
Packit 34410b
		return g_dbus_create_error(msg,
Packit 34410b
				ERROR_INTERFACE ".HealthError",
Packit 34410b
				"Can't get a valid id for the application");
Packit 34410b
	}
Packit 34410b
Packit 34410b
	app->oname = g_strdup(name);
Packit 34410b
Packit 34410b
	applications = g_slist_prepend(applications, app);
Packit 34410b
Packit 34410b
	app->dbus_watcher =
Packit 34410b
			g_dbus_add_disconnect_watch(btd_get_dbus_connection(),
Packit 34410b
					name, client_disconnected, app, NULL);
Packit 34410b
	g_slist_foreach(adapters, update_adapter_cb, NULL);
Packit 34410b
Packit 34410b
	DBG("Health application created with id %s", app->path);
Packit 34410b
Packit 34410b
	return g_dbus_create_reply(msg, DBUS_TYPE_OBJECT_PATH, &app->path,
Packit 34410b
							DBUS_TYPE_INVALID);
Packit 34410b
}
Packit 34410b
Packit 34410b
static DBusMessage *manager_destroy_application(DBusConnection *conn,
Packit 34410b
					DBusMessage *msg, void *user_data)
Packit 34410b
{
Packit 34410b
	const char *path;
Packit 34410b
	struct hdp_application *app;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
Packit 34410b
						DBUS_TYPE_INVALID))
Packit 34410b
		return btd_error_invalid_args(msg);
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(applications, path, cmp_app);
Packit 34410b
Packit 34410b
	if (l == NULL)
Packit 34410b
		return g_dbus_create_error(msg,
Packit 34410b
					ERROR_INTERFACE ".InvalidArguments",
Packit 34410b
					"Invalid arguments in method call, "
Packit 34410b
					"no such application");
Packit 34410b
Packit 34410b
	app = l->data;
Packit 34410b
	applications = g_slist_remove(applications, app);
Packit 34410b
Packit 34410b
	remove_application(app);
Packit 34410b
Packit 34410b
	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void application_unref(void *data, void *user_data)
Packit 34410b
{
Packit 34410b
	hdp_application_unref(data);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void manager_path_unregister(gpointer data)
Packit 34410b
{
Packit 34410b
	g_slist_foreach(applications, application_unref, NULL);
Packit 34410b
Packit 34410b
	g_slist_free(applications);
Packit 34410b
	applications = NULL;
Packit 34410b
Packit 34410b
	g_slist_foreach(adapters, update_adapter_cb, NULL);
Packit 34410b
}
Packit 34410b
Packit 34410b
static const GDBusMethodTable health_manager_methods[] = {
Packit 34410b
	{ GDBUS_METHOD("CreateApplication",
Packit 34410b
			GDBUS_ARGS({ "config", "a{sv}" }),
Packit 34410b
			GDBUS_ARGS({ "application", "o" }),
Packit 34410b
			manager_create_application) },
Packit 34410b
	{ GDBUS_METHOD("DestroyApplication",
Packit 34410b
			GDBUS_ARGS({ "application", "o" }), NULL,
Packit 34410b
			manager_destroy_application) },
Packit 34410b
	{ }
Packit 34410b
};
Packit 34410b
Packit 34410b
static gboolean channel_property_get_device(const GDBusPropertyTable *property,
Packit 34410b
					DBusMessageIter *iter, void *data)
Packit 34410b
{
Packit 34410b
	struct hdp_channel *chan = data;
Packit 34410b
	const char *path = device_get_path(chan->dev->dev);
Packit 34410b
Packit 34410b
	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
Packit 34410b
Packit 34410b
	return TRUE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean channel_property_get_application(
Packit 34410b
					const GDBusPropertyTable *property,
Packit 34410b
					DBusMessageIter *iter, void *data)
Packit 34410b
{
Packit 34410b
	struct hdp_channel *chan = data;
Packit 34410b
	const char *path = chan->app->path;
Packit 34410b
Packit 34410b
	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
Packit 34410b
Packit 34410b
	return TRUE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean channel_property_get_type(const GDBusPropertyTable *property,
Packit 34410b
					DBusMessageIter *iter, void *data)
Packit 34410b
{
Packit 34410b
	struct hdp_channel *chan = data;
Packit 34410b
	const char *type;
Packit 34410b
Packit 34410b
	if (chan->config == HDP_RELIABLE_DC)
Packit 34410b
		type = "reliable";
Packit 34410b
	else
Packit 34410b
		type = "streaming";
Packit 34410b
Packit 34410b
	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type);
Packit 34410b
Packit 34410b
	return TRUE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_tmp_dc_data_destroy(gpointer data)
Packit 34410b
{
Packit 34410b
	struct hdp_tmp_dc_data *hdp_conn = data;
Packit 34410b
Packit 34410b
	hdp_tmp_dc_data_unref(hdp_conn);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void abort_mdl_cb(GError *err, gpointer data)
Packit 34410b
{
Packit 34410b
	if (err != NULL)
Packit 34410b
		error("Aborting error: %s", err->message);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_mdl_reconn_cb(struct mcap_mdl *mdl, GError *err, gpointer data)
Packit 34410b
{
Packit 34410b
	DBusConnection *conn = btd_get_dbus_connection();
Packit 34410b
	struct hdp_tmp_dc_data *dc_data = data;
Packit 34410b
	DBusMessage *reply;
Packit 34410b
	int fd;
Packit 34410b
Packit 34410b
	if (err != NULL) {
Packit 34410b
		struct hdp_channel *chan = dc_data->hdp_chann;
Packit 34410b
		GError *gerr = NULL;
Packit 34410b
Packit 34410b
		error("%s", err->message);
Packit 34410b
		reply = g_dbus_create_error(dc_data->msg,
Packit 34410b
					ERROR_INTERFACE ".HealthError",
Packit 34410b
					"Cannot reconnect: %s", err->message);
Packit 34410b
		g_dbus_send_message(conn, reply);
Packit 34410b
Packit 34410b
		/* Send abort request because remote side */
Packit 34410b
		/* is now in PENDING state */
Packit 34410b
		if (!mcap_mdl_abort(chan->mdl, abort_mdl_cb, NULL, NULL,
Packit 34410b
								&gerr)) {
Packit 34410b
			error("%s", gerr->message);
Packit 34410b
			g_error_free(gerr);
Packit 34410b
		}
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	fd = mcap_mdl_get_fd(dc_data->hdp_chann->mdl);
Packit 34410b
	if (fd < 0) {
Packit 34410b
		reply = g_dbus_create_error(dc_data->msg,
Packit 34410b
						ERROR_INTERFACE ".HealthError",
Packit 34410b
						"Cannot get file descriptor");
Packit 34410b
		g_dbus_send_message(conn, reply);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	reply = g_dbus_create_reply(dc_data->msg, DBUS_TYPE_UNIX_FD,
Packit 34410b
							&fd, DBUS_TYPE_INVALID);
Packit 34410b
	g_dbus_send_message(conn, reply);
Packit 34410b
Packit 34410b
	g_dbus_emit_signal(conn, device_get_path(dc_data->hdp_chann->dev->dev),
Packit 34410b
			HEALTH_DEVICE, "ChannelConnected",
Packit 34410b
			DBUS_TYPE_OBJECT_PATH, &dc_data->hdp_chann->path,
Packit 34410b
			DBUS_TYPE_INVALID);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_get_dcpsm_cb(uint16_t dcpsm, gpointer user_data, GError *err)
Packit 34410b
{
Packit 34410b
	struct hdp_tmp_dc_data *hdp_conn = user_data;
Packit 34410b
	struct hdp_channel *hdp_chann = hdp_conn->hdp_chann;
Packit 34410b
	GError *gerr = NULL;
Packit 34410b
	uint8_t mode;
Packit 34410b
Packit 34410b
	if (err != NULL) {
Packit 34410b
		hdp_conn->cb(hdp_chann->mdl, err, hdp_conn);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (hdp_chann->config == HDP_RELIABLE_DC)
Packit 34410b
		mode = L2CAP_MODE_ERTM;
Packit 34410b
	else
Packit 34410b
		mode = L2CAP_MODE_STREAMING;
Packit 34410b
Packit 34410b
	if (mcap_connect_mdl(hdp_chann->mdl, mode, dcpsm, hdp_conn->cb,
Packit 34410b
					hdp_tmp_dc_data_ref(hdp_conn),
Packit 34410b
					hdp_tmp_dc_data_destroy, &gerr))
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	hdp_conn->cb(hdp_chann->mdl, err, hdp_conn);
Packit 34410b
	g_error_free(gerr);
Packit 34410b
	hdp_tmp_dc_data_unref(hdp_conn);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void device_reconnect_mdl_cb(struct mcap_mdl *mdl, GError *err,
Packit 34410b
								gpointer data)
Packit 34410b
{
Packit 34410b
	DBusConnection *conn = btd_get_dbus_connection();
Packit 34410b
	struct hdp_tmp_dc_data *dc_data = data;
Packit 34410b
	GError *gerr = NULL;
Packit 34410b
	DBusMessage *reply;
Packit 34410b
Packit 34410b
	if (err != NULL) {
Packit 34410b
		reply = g_dbus_create_error(dc_data->msg,
Packit 34410b
					ERROR_INTERFACE ".HealthError",
Packit 34410b
					"Cannot reconnect: %s", err->message);
Packit 34410b
		g_dbus_send_message(conn, reply);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	dc_data->cb = hdp_mdl_reconn_cb;
Packit 34410b
Packit 34410b
	if (hdp_get_dcpsm(dc_data->hdp_chann->dev, hdp_get_dcpsm_cb,
Packit 34410b
					hdp_tmp_dc_data_ref(dc_data),
Packit 34410b
					hdp_tmp_dc_data_destroy, &gerr))
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	error("%s", gerr->message);
Packit 34410b
Packit 34410b
	reply = g_dbus_create_error(dc_data->msg,
Packit 34410b
					ERROR_INTERFACE ".HealthError",
Packit 34410b
					"Cannot reconnect: %s", gerr->message);
Packit 34410b
	g_dbus_send_message(conn, reply);
Packit 34410b
	hdp_tmp_dc_data_unref(dc_data);
Packit 34410b
	g_error_free(gerr);
Packit 34410b
Packit 34410b
	/* Send abort request because remote side is now in PENDING state */
Packit 34410b
	if (!mcap_mdl_abort(mdl, abort_mdl_cb, NULL, NULL, &gerr)) {
Packit 34410b
		error("%s", gerr->message);
Packit 34410b
		g_error_free(gerr);
Packit 34410b
	}
Packit 34410b
}
Packit 34410b
Packit 34410b
static DBusMessage *channel_acquire_continue(struct hdp_tmp_dc_data *data,
Packit 34410b
								GError *err)
Packit 34410b
{
Packit 34410b
	DBusMessage *reply;
Packit 34410b
	GError *gerr = NULL;
Packit 34410b
	int fd;
Packit 34410b
Packit 34410b
	if (err != NULL) {
Packit 34410b
		return g_dbus_create_error(data->msg,
Packit 34410b
						ERROR_INTERFACE ".HealthError",
Packit 34410b
						"%s", err->message);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	fd = mcap_mdl_get_fd(data->hdp_chann->mdl);
Packit 34410b
	if (fd >= 0)
Packit 34410b
		return g_dbus_create_reply(data->msg, DBUS_TYPE_UNIX_FD, &fd,
Packit 34410b
							DBUS_TYPE_INVALID);
Packit 34410b
Packit 34410b
	hdp_tmp_dc_data_ref(data);
Packit 34410b
	if (mcap_reconnect_mdl(data->hdp_chann->mdl, device_reconnect_mdl_cb,
Packit 34410b
					data, hdp_tmp_dc_data_destroy, &gerr))
Packit 34410b
		return NULL;
Packit 34410b
Packit 34410b
	reply = g_dbus_create_error(data->msg, ERROR_INTERFACE ".HealthError",
Packit 34410b
					"Cannot reconnect: %s", gerr->message);
Packit 34410b
	g_error_free(gerr);
Packit 34410b
	hdp_tmp_dc_data_unref(data);
Packit 34410b
Packit 34410b
	return reply;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void channel_acquire_cb(gpointer data, GError *err)
Packit 34410b
{
Packit 34410b
	DBusMessage *reply;
Packit 34410b
Packit 34410b
	reply = channel_acquire_continue(data, err);
Packit 34410b
Packit 34410b
	if (reply != NULL)
Packit 34410b
		g_dbus_send_message(btd_get_dbus_connection(), reply);
Packit 34410b
}
Packit 34410b
Packit 34410b
static DBusMessage *channel_acquire(DBusConnection *conn,
Packit 34410b
					DBusMessage *msg, void *user_data)
Packit 34410b
{
Packit 34410b
	struct hdp_channel *chan = user_data;
Packit 34410b
	struct hdp_tmp_dc_data *dc_data;
Packit 34410b
	GError *gerr = NULL;
Packit 34410b
	DBusMessage *reply;
Packit 34410b
Packit 34410b
	dc_data = g_new0(struct hdp_tmp_dc_data, 1);
Packit 34410b
	dc_data->msg = dbus_message_ref(msg);
Packit 34410b
	dc_data->hdp_chann = hdp_channel_ref(chan);
Packit 34410b
Packit 34410b
	if (chan->dev->mcl_conn) {
Packit 34410b
		reply = channel_acquire_continue(hdp_tmp_dc_data_ref(dc_data),
Packit 34410b
									NULL);
Packit 34410b
		hdp_tmp_dc_data_unref(dc_data);
Packit 34410b
		return reply;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (hdp_establish_mcl(chan->dev, channel_acquire_cb,
Packit 34410b
						hdp_tmp_dc_data_ref(dc_data),
Packit 34410b
						hdp_tmp_dc_data_destroy, &gerr))
Packit 34410b
		return NULL;
Packit 34410b
Packit 34410b
	reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
Packit 34410b
					"%s", gerr->message);
Packit 34410b
	hdp_tmp_dc_data_unref(dc_data);
Packit 34410b
	g_error_free(gerr);
Packit 34410b
Packit 34410b
	return reply;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void close_mdl(struct hdp_channel *hdp_chann)
Packit 34410b
{
Packit 34410b
	int fd;
Packit 34410b
Packit 34410b
	fd = mcap_mdl_get_fd(hdp_chann->mdl);
Packit 34410b
	if (fd < 0)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	close(fd);
Packit 34410b
}
Packit 34410b
Packit 34410b
static DBusMessage *channel_release(DBusConnection *conn,
Packit 34410b
					DBusMessage *msg, void *user_data)
Packit 34410b
{
Packit 34410b
	struct hdp_channel *hdp_chann = user_data;
Packit 34410b
Packit 34410b
	close_mdl(hdp_chann);
Packit 34410b
Packit 34410b
	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void free_echo_data(struct hdp_echo_data *edata)
Packit 34410b
{
Packit 34410b
	if (edata == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	if (edata->tid > 0)
Packit 34410b
		g_source_remove(edata->tid);
Packit 34410b
Packit 34410b
	if (edata->buf != NULL)
Packit 34410b
		g_free(edata->buf);
Packit 34410b
Packit 34410b
Packit 34410b
	g_free(edata);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void health_channel_destroy(void *data)
Packit 34410b
{
Packit 34410b
	struct hdp_channel *hdp_chan = data;
Packit 34410b
	struct hdp_device *dev = hdp_chan->dev;
Packit 34410b
Packit 34410b
	DBG("Destroy Health Channel %s", hdp_chan->path);
Packit 34410b
	if (g_slist_find(dev->channels, hdp_chan) == NULL)
Packit 34410b
		goto end;
Packit 34410b
Packit 34410b
	dev->channels = g_slist_remove(dev->channels, hdp_chan);
Packit 34410b
Packit 34410b
	if (hdp_chan->mdep != HDP_MDEP_ECHO)
Packit 34410b
		g_dbus_emit_signal(btd_get_dbus_connection(),
Packit 34410b
					device_get_path(dev->dev),
Packit 34410b
					HEALTH_DEVICE, "ChannelDeleted",
Packit 34410b
					DBUS_TYPE_OBJECT_PATH, &hdp_chan->path,
Packit 34410b
					DBUS_TYPE_INVALID);
Packit 34410b
Packit 34410b
	if (hdp_chan == dev->fr) {
Packit 34410b
		hdp_channel_unref(dev->fr);
Packit 34410b
		dev->fr = NULL;
Packit 34410b
	}
Packit 34410b
Packit 34410b
end:
Packit 34410b
	hdp_channel_unref(hdp_chan);
Packit 34410b
}
Packit 34410b
Packit 34410b
static const GDBusMethodTable health_channels_methods[] = {
Packit 34410b
	{ GDBUS_ASYNC_METHOD("Acquire",
Packit 34410b
			NULL, GDBUS_ARGS({ "fd", "h" }),
Packit 34410b
			channel_acquire) },
Packit 34410b
	{ GDBUS_METHOD("Release", NULL, NULL, channel_release) },
Packit 34410b
	{ }
Packit 34410b
};
Packit 34410b
Packit 34410b
static const GDBusPropertyTable health_channels_properties[] = {
Packit 34410b
	{ "Device", "o",  channel_property_get_device },
Packit 34410b
	{ "Application", "o", channel_property_get_application },
Packit 34410b
	{ "Type", "s", channel_property_get_type },
Packit 34410b
	{ }
Packit 34410b
};
Packit 34410b
Packit 34410b
static struct hdp_channel *create_channel(struct hdp_device *dev,
Packit 34410b
						uint8_t config,
Packit 34410b
						struct mcap_mdl *mdl,
Packit 34410b
						uint16_t mdlid,
Packit 34410b
						struct hdp_application *app,
Packit 34410b
						GError **err)
Packit 34410b
{
Packit 34410b
	struct hdp_channel *hdp_chann;
Packit 34410b
Packit 34410b
	if (dev == NULL) {
Packit 34410b
		g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
Packit 34410b
					"HDP device uninitialized");
Packit 34410b
		return NULL;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	hdp_chann = g_new0(struct hdp_channel, 1);
Packit 34410b
	hdp_chann->config = config;
Packit 34410b
	hdp_chann->dev = health_device_ref(dev);
Packit 34410b
	hdp_chann->mdlid = mdlid;
Packit 34410b
Packit 34410b
	if (mdl != NULL)
Packit 34410b
		hdp_chann->mdl = mcap_mdl_ref(mdl);
Packit 34410b
Packit 34410b
	if (app != NULL) {
Packit 34410b
		hdp_chann->mdep = app->id;
Packit 34410b
		hdp_chann->app = hdp_application_ref(app);
Packit 34410b
	} else
Packit 34410b
		hdp_chann->edata = g_new0(struct hdp_echo_data, 1);
Packit 34410b
Packit 34410b
	hdp_chann->path = g_strdup_printf("%s/chan%d",
Packit 34410b
					device_get_path(hdp_chann->dev->dev),
Packit 34410b
					hdp_chann->mdlid);
Packit 34410b
Packit 34410b
	dev->channels = g_slist_append(dev->channels,
Packit 34410b
						hdp_channel_ref(hdp_chann));
Packit 34410b
Packit 34410b
	if (hdp_chann->mdep == HDP_MDEP_ECHO)
Packit 34410b
		return hdp_channel_ref(hdp_chann);
Packit 34410b
Packit 34410b
	if (!g_dbus_register_interface(btd_get_dbus_connection(),
Packit 34410b
					hdp_chann->path, HEALTH_CHANNEL,
Packit 34410b
					health_channels_methods, NULL,
Packit 34410b
					health_channels_properties, hdp_chann,
Packit 34410b
					health_channel_destroy)) {
Packit 34410b
		g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
Packit 34410b
					"Can't register the channel interface");
Packit 34410b
		health_channel_destroy(hdp_chann);
Packit 34410b
		return NULL;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	return hdp_channel_ref(hdp_chann);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void remove_channels(struct hdp_device *dev)
Packit 34410b
{
Packit 34410b
	struct hdp_channel *chan;
Packit 34410b
	char *path;
Packit 34410b
Packit 34410b
	while (dev->channels != NULL) {
Packit 34410b
		chan = dev->channels->data;
Packit 34410b
Packit 34410b
		path = g_strdup(chan->path);
Packit 34410b
		if (!g_dbus_unregister_interface(btd_get_dbus_connection(),
Packit 34410b
							path, HEALTH_CHANNEL))
Packit 34410b
			health_channel_destroy(chan);
Packit 34410b
		g_free(path);
Packit 34410b
	}
Packit 34410b
}
Packit 34410b
Packit 34410b
static void close_device_con(struct hdp_device *dev, gboolean cache)
Packit 34410b
{
Packit 34410b
	if (dev->mcl == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	mcap_close_mcl(dev->mcl, cache);
Packit 34410b
	dev->mcl_conn = FALSE;
Packit 34410b
Packit 34410b
	if (cache)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	device_unref_mcl(dev);
Packit 34410b
	remove_channels(dev);
Packit 34410b
Packit 34410b
	if (!dev->sdp_present) {
Packit 34410b
		const char *path;
Packit 34410b
Packit 34410b
		path = device_get_path(dev->dev);
Packit 34410b
		g_dbus_unregister_interface(btd_get_dbus_connection(),
Packit 34410b
							path, HEALTH_DEVICE);
Packit 34410b
	}
Packit 34410b
}
Packit 34410b
Packit 34410b
static int send_echo_data(int sock, const void *buf, uint32_t size)
Packit 34410b
{
Packit 34410b
	const uint8_t *buf_b = buf;
Packit 34410b
	uint32_t sent = 0;
Packit 34410b
Packit 34410b
	while (sent < size) {
Packit 34410b
		int n = write(sock, buf_b + sent, size - sent);
Packit 34410b
		if (n < 0)
Packit 34410b
			return -1;
Packit 34410b
		sent += n;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	return 0;
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean serve_echo(GIOChannel *io_chan, GIOCondition cond,
Packit 34410b
								gpointer data)
Packit 34410b
{
Packit 34410b
	struct hdp_channel *chan = data;
Packit 34410b
	uint8_t buf[MCAP_DC_MTU];
Packit 34410b
	int fd, len;
Packit 34410b
Packit 34410b
	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
Packit 34410b
		hdp_channel_unref(chan);
Packit 34410b
		return FALSE;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (chan->edata->echo_done)
Packit 34410b
		goto fail;
Packit 34410b
Packit 34410b
	chan->edata->echo_done = TRUE;
Packit 34410b
Packit 34410b
	fd = g_io_channel_unix_get_fd(io_chan);
Packit 34410b
Packit 34410b
	len = read(fd, buf, sizeof(buf));
Packit 34410b
	if (len < 0)
Packit 34410b
		goto fail;
Packit 34410b
Packit 34410b
	if (send_echo_data(fd, buf, len)  >= 0)
Packit 34410b
		return TRUE;
Packit 34410b
Packit 34410b
fail:
Packit 34410b
	close_device_con(chan->dev, FALSE);
Packit 34410b
	hdp_channel_unref(chan);
Packit 34410b
	return FALSE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean check_channel_conf(struct hdp_channel *chan)
Packit 34410b
{
Packit 34410b
	GError *err = NULL;
Packit 34410b
	GIOChannel *io;
Packit 34410b
	uint8_t mode;
Packit 34410b
	uint16_t imtu, omtu;
Packit 34410b
	int fd;
Packit 34410b
Packit 34410b
	fd = mcap_mdl_get_fd(chan->mdl);
Packit 34410b
	if (fd < 0)
Packit 34410b
		return FALSE;
Packit 34410b
	io = g_io_channel_unix_new(fd);
Packit 34410b
Packit 34410b
	if (!bt_io_get(io, &err,
Packit 34410b
			BT_IO_OPT_MODE, &mode,
Packit 34410b
			BT_IO_OPT_IMTU, &imtu,
Packit 34410b
			BT_IO_OPT_OMTU, &omtu,
Packit 34410b
			BT_IO_OPT_INVALID)) {
Packit 34410b
		error("Error: %s", err->message);
Packit 34410b
		g_io_channel_unref(io);
Packit 34410b
		g_error_free(err);
Packit 34410b
		return FALSE;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	g_io_channel_unref(io);
Packit 34410b
Packit 34410b
	switch (chan->config) {
Packit 34410b
	case HDP_RELIABLE_DC:
Packit 34410b
		if (mode != L2CAP_MODE_ERTM)
Packit 34410b
			return FALSE;
Packit 34410b
		break;
Packit 34410b
	case HDP_STREAMING_DC:
Packit 34410b
		if (mode != L2CAP_MODE_STREAMING)
Packit 34410b
			return FALSE;
Packit 34410b
		break;
Packit 34410b
	default:
Packit 34410b
		error("Error: Connected with unknown configuration");
Packit 34410b
		return FALSE;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	DBG("MDL imtu %d omtu %d Channel imtu %d omtu %d", imtu, omtu,
Packit 34410b
						chan->imtu, chan->omtu);
Packit 34410b
Packit 34410b
	if (chan->imtu == 0)
Packit 34410b
		chan->imtu = imtu;
Packit 34410b
	if (chan->omtu == 0)
Packit 34410b
		chan->omtu = omtu;
Packit 34410b
Packit 34410b
	if (chan->imtu != imtu || chan->omtu != omtu)
Packit 34410b
		return FALSE;
Packit 34410b
Packit 34410b
	return TRUE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *dev = data;
Packit 34410b
	struct hdp_channel *chan;
Packit 34410b
Packit 34410b
	DBG("");
Packit 34410b
Packit 34410b
	if (dev->ndc == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	chan = dev->ndc;
Packit 34410b
	if (chan->mdl == NULL)
Packit 34410b
		chan->mdl = mcap_mdl_ref(mdl);
Packit 34410b
Packit 34410b
	if (g_slist_find(dev->channels, chan) == NULL)
Packit 34410b
		dev->channels = g_slist_prepend(dev->channels,
Packit 34410b
							hdp_channel_ref(chan));
Packit 34410b
Packit 34410b
	if (!check_channel_conf(chan)) {
Packit 34410b
		close_mdl(chan);
Packit 34410b
		goto end;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (chan->mdep == HDP_MDEP_ECHO) {
Packit 34410b
		GIOChannel *io;
Packit 34410b
		int fd;
Packit 34410b
Packit 34410b
		fd = mcap_mdl_get_fd(chan->mdl);
Packit 34410b
		if (fd < 0)
Packit 34410b
			goto end;
Packit 34410b
Packit 34410b
		chan->edata->echo_done = FALSE;
Packit 34410b
		io = g_io_channel_unix_new(fd);
Packit 34410b
		g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN,
Packit 34410b
				serve_echo, hdp_channel_ref(chan));
Packit 34410b
		g_io_channel_unref(io);
Packit 34410b
		goto end;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	g_dbus_emit_signal(btd_get_dbus_connection(), device_get_path(dev->dev),
Packit 34410b
				HEALTH_DEVICE, "ChannelConnected",
Packit 34410b
				DBUS_TYPE_OBJECT_PATH, &chan->path,
Packit 34410b
				DBUS_TYPE_INVALID);
Packit 34410b
Packit 34410b
	if (dev->fr != NULL)
Packit 34410b
		goto end;
Packit 34410b
Packit 34410b
	dev->fr = hdp_channel_ref(chan);
Packit 34410b
Packit 34410b
	g_dbus_emit_property_changed(btd_get_dbus_connection(),
Packit 34410b
				device_get_path(dev->dev), HEALTH_DEVICE,
Packit 34410b
				"MainChannel");
Packit 34410b
Packit 34410b
end:
Packit 34410b
	hdp_channel_unref(dev->ndc);
Packit 34410b
	dev->ndc = NULL;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data)
Packit 34410b
{
Packit 34410b
	/* struct hdp_device *dev = data; */
Packit 34410b
Packit 34410b
	DBG("");
Packit 34410b
Packit 34410b
	/* Nothing to do */
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_mcap_mdl_deleted_cb(struct mcap_mdl *mdl, void *data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *dev = data;
Packit 34410b
	struct hdp_channel *chan;
Packit 34410b
	char *path;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	DBG("");
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(dev->channels, mdl, cmp_chan_mdl);
Packit 34410b
	if (l == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	chan = l->data;
Packit 34410b
Packit 34410b
	path = g_strdup(chan->path);
Packit 34410b
	if (!g_dbus_unregister_interface(btd_get_dbus_connection(),
Packit 34410b
							path, HEALTH_CHANNEL))
Packit 34410b
		health_channel_destroy(chan);
Packit 34410b
	g_free(path);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *dev = data;
Packit 34410b
Packit 34410b
	DBG("");
Packit 34410b
Packit 34410b
	if (dev->ndc == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	dev->ndc->mdl = mcap_mdl_ref(mdl);
Packit 34410b
Packit 34410b
	if (g_slist_find(dev->channels, dev->ndc) == NULL)
Packit 34410b
		dev->channels = g_slist_prepend(dev->channels,
Packit 34410b
						hdp_channel_ref(dev->ndc));
Packit 34410b
Packit 34410b
	if (dev->ndc->mdep != HDP_MDEP_ECHO)
Packit 34410b
		g_dbus_emit_signal(btd_get_dbus_connection(),
Packit 34410b
					device_get_path(dev->dev),
Packit 34410b
					HEALTH_DEVICE, "ChannelConnected",
Packit 34410b
					DBUS_TYPE_OBJECT_PATH, &dev->ndc->path,
Packit 34410b
					DBUS_TYPE_INVALID);
Packit 34410b
Packit 34410b
	hdp_channel_unref(dev->ndc);
Packit 34410b
	dev->ndc = NULL;
Packit 34410b
}
Packit 34410b
Packit 34410b
static uint8_t hdp2l2cap_mode(uint8_t hdp_mode)
Packit 34410b
{
Packit 34410b
	return hdp_mode == HDP_STREAMING_DC ? L2CAP_MODE_STREAMING :
Packit 34410b
								L2CAP_MODE_ERTM;
Packit 34410b
}
Packit 34410b
Packit 34410b
static uint8_t hdp_mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
Packit 34410b
				uint16_t mdlid, uint8_t *conf, void *data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *dev = data;
Packit 34410b
	struct hdp_application *app;
Packit 34410b
	GError *err = NULL;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	DBG("Data channel request");
Packit 34410b
Packit 34410b
	if (mdepid == HDP_MDEP_ECHO) {
Packit 34410b
		switch (*conf) {
Packit 34410b
		case HDP_NO_PREFERENCE_DC:
Packit 34410b
			*conf = HDP_RELIABLE_DC;
Packit 34410b
			break;
Packit 34410b
		case HDP_RELIABLE_DC:
Packit 34410b
			break;
Packit 34410b
		case HDP_STREAMING_DC:
Packit 34410b
			return MCAP_CONFIGURATION_REJECTED;
Packit 34410b
		default:
Packit 34410b
			/* Special case defined in HDP spec 3.4. When an invalid
Packit 34410b
			* configuration is received we shall close the MCL when
Packit 34410b
			* we are still processing the callback. */
Packit 34410b
			close_device_con(dev, FALSE);
Packit 34410b
			return MCAP_CONFIGURATION_REJECTED; /* not processed */
Packit 34410b
		}
Packit 34410b
Packit 34410b
		if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi,
Packit 34410b
						L2CAP_MODE_ERTM, &err)) {
Packit 34410b
			error("Error: %s", err->message);
Packit 34410b
			g_error_free(err);
Packit 34410b
			return MCAP_MDL_BUSY;
Packit 34410b
		}
Packit 34410b
Packit 34410b
		dev->ndc = create_channel(dev, *conf, NULL, mdlid, NULL, NULL);
Packit 34410b
		if (dev->ndc == NULL)
Packit 34410b
			return MCAP_MDL_BUSY;
Packit 34410b
Packit 34410b
		return MCAP_SUCCESS;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(applications, &mdepid, cmp_app_id);
Packit 34410b
	if (l == NULL)
Packit 34410b
		return MCAP_INVALID_MDEP;
Packit 34410b
Packit 34410b
	app = l->data;
Packit 34410b
Packit 34410b
	/* Check if is the first dc if so,
Packit 34410b
	* only reliable configuration is allowed */
Packit 34410b
	switch (*conf) {
Packit 34410b
	case HDP_NO_PREFERENCE_DC:
Packit 34410b
		if (app->role == HDP_SINK)
Packit 34410b
			return MCAP_CONFIGURATION_REJECTED;
Packit 34410b
		else if (dev->fr && app->chan_type_set)
Packit 34410b
			*conf = app->chan_type;
Packit 34410b
		else
Packit 34410b
			*conf = HDP_RELIABLE_DC;
Packit 34410b
		break;
Packit 34410b
	case HDP_STREAMING_DC:
Packit 34410b
		if (!dev->fr || app->role == HDP_SOURCE)
Packit 34410b
			return MCAP_CONFIGURATION_REJECTED;
Packit 34410b
		break;
Packit 34410b
	case HDP_RELIABLE_DC:
Packit 34410b
		if (app->role == HDP_SOURCE)
Packit 34410b
			return MCAP_CONFIGURATION_REJECTED;
Packit 34410b
		break;
Packit 34410b
	default:
Packit 34410b
		/* Special case defined in HDP spec 3.4. When an invalid
Packit 34410b
		* configuration is received we shall close the MCL when
Packit 34410b
		* we are still processing the callback. */
Packit 34410b
		close_device_con(dev, FALSE);
Packit 34410b
		return MCAP_CONFIGURATION_REJECTED; /* not processed */
Packit 34410b
	}
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(dev->channels, &mdlid, cmp_chan_mdlid);
Packit 34410b
	if (l != NULL) {
Packit 34410b
		struct hdp_channel *chan = l->data;
Packit 34410b
		char *path;
Packit 34410b
Packit 34410b
		path = g_strdup(chan->path);
Packit 34410b
		g_dbus_unregister_interface(btd_get_dbus_connection(),
Packit 34410b
							path, HEALTH_CHANNEL);
Packit 34410b
		g_free(path);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi,
Packit 34410b
						hdp2l2cap_mode(*conf), &err)) {
Packit 34410b
		error("Error: %s", err->message);
Packit 34410b
		g_error_free(err);
Packit 34410b
		return MCAP_MDL_BUSY;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	dev->ndc = create_channel(dev, *conf, NULL, mdlid, app, NULL);
Packit 34410b
	if (dev->ndc == NULL)
Packit 34410b
		return MCAP_MDL_BUSY;
Packit 34410b
Packit 34410b
	return MCAP_SUCCESS;
Packit 34410b
}
Packit 34410b
Packit 34410b
static uint8_t hdp_mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *dev = data;
Packit 34410b
	struct hdp_channel *chan;
Packit 34410b
	GError *err = NULL;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(dev->channels, mdl, cmp_chan_mdl);
Packit 34410b
	if (l == NULL)
Packit 34410b
		return MCAP_INVALID_MDL;
Packit 34410b
Packit 34410b
	chan = l->data;
Packit 34410b
Packit 34410b
	if (dev->fr == NULL && chan->config != HDP_RELIABLE_DC &&
Packit 34410b
						chan->mdep != HDP_MDEP_ECHO)
Packit 34410b
		return MCAP_UNSPECIFIED_ERROR;
Packit 34410b
Packit 34410b
	if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi,
Packit 34410b
					hdp2l2cap_mode(chan->config), &err)) {
Packit 34410b
		error("Error: %s", err->message);
Packit 34410b
		g_error_free(err);
Packit 34410b
		return MCAP_MDL_BUSY;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	dev->ndc = hdp_channel_ref(chan);
Packit 34410b
Packit 34410b
	return MCAP_SUCCESS;
Packit 34410b
}
Packit 34410b
Packit 34410b
gboolean hdp_set_mcl_cb(struct hdp_device *device, GError **err)
Packit 34410b
{
Packit 34410b
	gboolean ret;
Packit 34410b
Packit 34410b
	if (device->mcl == NULL)
Packit 34410b
		return FALSE;
Packit 34410b
Packit 34410b
	ret = mcap_mcl_set_cb(device->mcl, device, err,
Packit 34410b
		MCAP_MDL_CB_CONNECTED, hdp_mcap_mdl_connected_cb,
Packit 34410b
		MCAP_MDL_CB_CLOSED, hdp_mcap_mdl_closed_cb,
Packit 34410b
		MCAP_MDL_CB_DELETED, hdp_mcap_mdl_deleted_cb,
Packit 34410b
		MCAP_MDL_CB_ABORTED, hdp_mcap_mdl_aborted_cb,
Packit 34410b
		MCAP_MDL_CB_REMOTE_CONN_REQ, hdp_mcap_mdl_conn_req_cb,
Packit 34410b
		MCAP_MDL_CB_REMOTE_RECONN_REQ, hdp_mcap_mdl_reconn_req_cb,
Packit 34410b
		MCAP_MDL_CB_INVALID);
Packit 34410b
	if (ret)
Packit 34410b
		return TRUE;
Packit 34410b
Packit 34410b
	error("Can't set mcl callbacks, closing mcl");
Packit 34410b
	close_device_con(device, TRUE);
Packit 34410b
Packit 34410b
	return FALSE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *hdp_device;
Packit 34410b
	bdaddr_t addr;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	mcap_mcl_get_addr(mcl, &addr);
Packit 34410b
	l = g_slist_find_custom(devices, &addr, cmp_dev_addr);
Packit 34410b
	if (l == NULL) {
Packit 34410b
		struct hdp_adapter *hdp_adapter = data;
Packit 34410b
		struct btd_device *device;
Packit 34410b
Packit 34410b
		device = btd_adapter_get_device(hdp_adapter->btd_adapter,
Packit 34410b
							&addr, BDADDR_BREDR);
Packit 34410b
		if (!device)
Packit 34410b
			return;
Packit 34410b
		hdp_device = create_health_device(device);
Packit 34410b
		if (!hdp_device)
Packit 34410b
			return;
Packit 34410b
		devices = g_slist_append(devices, hdp_device);
Packit 34410b
	} else
Packit 34410b
		hdp_device = l->data;
Packit 34410b
Packit 34410b
	hdp_device->mcl = mcap_mcl_ref(mcl);
Packit 34410b
	hdp_device->mcl_conn = TRUE;
Packit 34410b
Packit 34410b
	DBG("New mcl connected from  %s", device_get_path(hdp_device->dev));
Packit 34410b
Packit 34410b
	hdp_set_mcl_cb(hdp_device, NULL);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *hdp_device;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
Packit 34410b
	if (l == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	hdp_device = l->data;
Packit 34410b
	hdp_device->mcl_conn = TRUE;
Packit 34410b
Packit 34410b
	DBG("MCL reconnected %s", device_get_path(hdp_device->dev));
Packit 34410b
Packit 34410b
	hdp_set_mcl_cb(hdp_device, NULL);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *hdp_device;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
Packit 34410b
	if (l == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	hdp_device = l->data;
Packit 34410b
	hdp_device->mcl_conn = FALSE;
Packit 34410b
Packit 34410b
	DBG("Mcl disconnected %s", device_get_path(hdp_device->dev));
Packit 34410b
}
Packit 34410b
Packit 34410b
static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *hdp_device;
Packit 34410b
	const char *path;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
Packit 34410b
	if (l == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	hdp_device = l->data;
Packit 34410b
	device_unref_mcl(hdp_device);
Packit 34410b
Packit 34410b
	if (hdp_device->sdp_present)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	/* Because remote device hasn't announced an HDP record */
Packit 34410b
	/* the Bluetooth daemon won't notify when the device shall */
Packit 34410b
	/* be removed. Then we have to remove the HealthDevice */
Packit 34410b
	/* interface manually */
Packit 34410b
	path = device_get_path(hdp_device->dev);
Packit 34410b
	g_dbus_unregister_interface(btd_get_dbus_connection(),
Packit 34410b
							path, HEALTH_DEVICE);
Packit 34410b
	DBG("Mcl uncached %s", path);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void check_devices_mcl(void)
Packit 34410b
{
Packit 34410b
	struct hdp_device *dev;
Packit 34410b
	GSList *l, *to_delete = NULL;
Packit 34410b
Packit 34410b
	for (l = devices; l; l = l->next) {
Packit 34410b
		dev = l->data;
Packit 34410b
		device_unref_mcl(dev);
Packit 34410b
Packit 34410b
		if (!dev->sdp_present)
Packit 34410b
			to_delete = g_slist_append(to_delete, dev);
Packit 34410b
		else
Packit 34410b
			remove_channels(dev);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	for (l = to_delete; l; l = l->next) {
Packit 34410b
		const char *path;
Packit 34410b
Packit 34410b
		path = device_get_path(dev->dev);
Packit 34410b
		g_dbus_unregister_interface(btd_get_dbus_connection(),
Packit 34410b
							path, HEALTH_DEVICE);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	g_slist_free(to_delete);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void release_adapter_instance(struct hdp_adapter *hdp_adapter)
Packit 34410b
{
Packit 34410b
	if (hdp_adapter->mi == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	check_devices_mcl();
Packit 34410b
	mcap_release_instance(hdp_adapter->mi);
Packit 34410b
	mcap_instance_unref(hdp_adapter->mi);
Packit 34410b
	hdp_adapter->mi = NULL;
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean update_adapter(struct hdp_adapter *hdp_adapter)
Packit 34410b
{
Packit 34410b
	GError *err = NULL;
Packit 34410b
	const bdaddr_t *src;
Packit 34410b
Packit 34410b
	if (applications == NULL) {
Packit 34410b
		release_adapter_instance(hdp_adapter);
Packit 34410b
		goto update;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (hdp_adapter->mi != NULL)
Packit 34410b
		goto update;
Packit 34410b
Packit 34410b
	src = btd_adapter_get_address(hdp_adapter->btd_adapter);
Packit 34410b
Packit 34410b
	hdp_adapter->mi = mcap_create_instance(src,
Packit 34410b
				BT_IO_SEC_MEDIUM, 0, 0,
Packit 34410b
				mcl_connected, mcl_reconnected,
Packit 34410b
				mcl_disconnected, mcl_uncached,
Packit 34410b
				NULL, /* CSP is not used by now */
Packit 34410b
				hdp_adapter, &err;;
Packit 34410b
	if (hdp_adapter->mi == NULL) {
Packit 34410b
		error("Error creating the MCAP instance: %s", err->message);
Packit 34410b
		g_error_free(err);
Packit 34410b
		return FALSE;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	hdp_adapter->ccpsm = mcap_get_ctrl_psm(hdp_adapter->mi, &err;;
Packit 34410b
	if (err != NULL) {
Packit 34410b
		error("Error getting MCAP control PSM: %s", err->message);
Packit 34410b
		goto fail;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	hdp_adapter->dcpsm = mcap_get_data_psm(hdp_adapter->mi, &err;;
Packit 34410b
	if (err != NULL) {
Packit 34410b
		error("Error getting MCAP data PSM: %s", err->message);
Packit 34410b
		goto fail;
Packit 34410b
	}
Packit 34410b
Packit 34410b
update:
Packit 34410b
	if (hdp_update_sdp_record(hdp_adapter, applications))
Packit 34410b
		return TRUE;
Packit 34410b
	error("Error updating the SDP record");
Packit 34410b
Packit 34410b
fail:
Packit 34410b
	release_adapter_instance(hdp_adapter);
Packit 34410b
	if (err != NULL)
Packit 34410b
		g_error_free(err);
Packit 34410b
Packit 34410b
	return FALSE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void update_adapter_cb(void *data, void *user_data)
Packit 34410b
{
Packit 34410b
	update_adapter(data);
Packit 34410b
}
Packit 34410b
Packit 34410b
int hdp_adapter_register(struct btd_adapter *adapter)
Packit 34410b
{
Packit 34410b
	struct hdp_adapter *hdp_adapter;
Packit 34410b
Packit 34410b
	hdp_adapter = g_new0(struct hdp_adapter, 1);
Packit 34410b
	hdp_adapter->btd_adapter = btd_adapter_ref(adapter);
Packit 34410b
Packit 34410b
	if(!update_adapter(hdp_adapter))
Packit 34410b
		goto fail;
Packit 34410b
Packit 34410b
	adapters = g_slist_append(adapters, hdp_adapter);
Packit 34410b
Packit 34410b
	return 0;
Packit 34410b
Packit 34410b
fail:
Packit 34410b
	btd_adapter_unref(hdp_adapter->btd_adapter);
Packit 34410b
	g_free(hdp_adapter);
Packit 34410b
	return -1;
Packit 34410b
}
Packit 34410b
Packit 34410b
void hdp_adapter_unregister(struct btd_adapter *adapter)
Packit 34410b
{
Packit 34410b
	struct hdp_adapter *hdp_adapter;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(adapters, adapter, cmp_adapter);
Packit 34410b
Packit 34410b
	if (l == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	hdp_adapter = l->data;
Packit 34410b
	adapters = g_slist_remove(adapters, hdp_adapter);
Packit 34410b
	if (hdp_adapter->sdp_handler > 0)
Packit 34410b
		adapter_service_remove(adapter, hdp_adapter->sdp_handler);
Packit 34410b
	release_adapter_instance(hdp_adapter);
Packit 34410b
	btd_adapter_unref(hdp_adapter->btd_adapter);
Packit 34410b
	g_free(hdp_adapter);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void delete_echo_channel_cb(GError *err, gpointer chan)
Packit 34410b
{
Packit 34410b
	if (err != NULL && err->code != MCAP_INVALID_MDL) {
Packit 34410b
		/* TODO: Decide if more action is required here */
Packit 34410b
		error("Error deleting echo channel: %s", err->message);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	health_channel_destroy(chan);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void delete_echo_channel(struct hdp_channel *chan)
Packit 34410b
{
Packit 34410b
	GError *err = NULL;
Packit 34410b
Packit 34410b
	if (!chan->dev->mcl_conn) {
Packit 34410b
		error("Echo channel cannot be deleted: mcl closed");
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (mcap_delete_mdl(chan->mdl, delete_echo_channel_cb,
Packit 34410b
				hdp_channel_ref(chan),
Packit 34410b
				(GDestroyNotify) hdp_channel_unref, &err))
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	hdp_channel_unref(chan);
Packit 34410b
	error("Error deleting the echo channel: %s", err->message);
Packit 34410b
	g_error_free(err);
Packit 34410b
Packit 34410b
	/* TODO: Decide if more action is required here */
Packit 34410b
}
Packit 34410b
Packit 34410b
static void abort_echo_channel_cb(GError *err, gpointer data)
Packit 34410b
{
Packit 34410b
	struct hdp_channel *chan = data;
Packit 34410b
Packit 34410b
	if (err != NULL && err->code != MCAP_ERROR_INVALID_OPERATION) {
Packit 34410b
		error("Aborting error: %s", err->message);
Packit 34410b
		if (err->code == MCAP_INVALID_MDL) {
Packit 34410b
			/* MDL is removed from MCAP so we can */
Packit 34410b
			/* free the data channel without sending */
Packit 34410b
			/* a MD_DELETE_MDL_REQ */
Packit 34410b
			/* TODO review the above comment */
Packit 34410b
			/* hdp_channel_unref(chan); */
Packit 34410b
		}
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	delete_echo_channel(chan);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void destroy_create_dc_data(gpointer data)
Packit 34410b
{
Packit 34410b
	struct hdp_create_dc *dc_data = data;
Packit 34410b
Packit 34410b
	hdp_create_data_unref(dc_data);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void *generate_echo_packet(void)
Packit 34410b
{
Packit 34410b
	uint8_t *buf;
Packit 34410b
	int i;
Packit 34410b
Packit 34410b
	buf = g_malloc(HDP_ECHO_LEN);
Packit 34410b
	srand(time(NULL));
Packit 34410b
Packit 34410b
	for(i = 0; i < HDP_ECHO_LEN; i++)
Packit 34410b
		buf[i] = rand() % UINT8_MAX;
Packit 34410b
Packit 34410b
	return buf;
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean check_echo(GIOChannel *io_chan, GIOCondition cond,
Packit 34410b
								gpointer data)
Packit 34410b
{
Packit 34410b
	struct hdp_tmp_dc_data *hdp_conn =  data;
Packit 34410b
	struct hdp_echo_data *edata = hdp_conn->hdp_chann->edata;
Packit 34410b
	struct hdp_channel *chan = hdp_conn->hdp_chann;
Packit 34410b
	uint8_t buf[MCAP_DC_MTU];
Packit 34410b
	DBusMessage *reply;
Packit 34410b
	gboolean value;
Packit 34410b
	int fd, len;
Packit 34410b
Packit 34410b
	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
Packit 34410b
		value = FALSE;
Packit 34410b
		goto end;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	fd = g_io_channel_unix_get_fd(io_chan);
Packit 34410b
Packit 34410b
	len = read(fd, buf, sizeof(buf));
Packit 34410b
	if (len != HDP_ECHO_LEN) {
Packit 34410b
		value = FALSE;
Packit 34410b
		goto end;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	value = (memcmp(buf, edata->buf, len) == 0);
Packit 34410b
Packit 34410b
end:
Packit 34410b
	reply = g_dbus_create_reply(hdp_conn->msg, DBUS_TYPE_BOOLEAN, &value,
Packit 34410b
							DBUS_TYPE_INVALID);
Packit 34410b
	g_dbus_send_message(btd_get_dbus_connection(), reply);
Packit 34410b
	g_source_remove(edata->tid);
Packit 34410b
	edata->tid = 0;
Packit 34410b
	g_free(edata->buf);
Packit 34410b
	edata->buf = NULL;
Packit 34410b
Packit 34410b
	if (!value)
Packit 34410b
		close_device_con(chan->dev, FALSE);
Packit 34410b
	else
Packit 34410b
		delete_echo_channel(chan);
Packit 34410b
	hdp_tmp_dc_data_unref(hdp_conn);
Packit 34410b
Packit 34410b
	return FALSE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean echo_timeout(gpointer data)
Packit 34410b
{
Packit 34410b
	struct hdp_channel *chan = data;
Packit 34410b
	GIOChannel *io;
Packit 34410b
	int fd;
Packit 34410b
Packit 34410b
	error("Error: Echo request timeout");
Packit 34410b
	chan->edata->tid = 0;
Packit 34410b
Packit 34410b
	fd = mcap_mdl_get_fd(chan->mdl);
Packit 34410b
	if (fd < 0)
Packit 34410b
		return FALSE;
Packit 34410b
Packit 34410b
	io = g_io_channel_unix_new(fd);
Packit 34410b
	g_io_channel_shutdown(io, TRUE, NULL);
Packit 34410b
Packit 34410b
	return FALSE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_echo_connect_cb(struct mcap_mdl *mdl, GError *err,
Packit 34410b
								gpointer data)
Packit 34410b
{
Packit 34410b
	DBusConnection *conn = btd_get_dbus_connection();
Packit 34410b
	struct hdp_tmp_dc_data *hdp_conn =  data;
Packit 34410b
	struct hdp_echo_data *edata;
Packit 34410b
	GError *gerr = NULL;
Packit 34410b
	DBusMessage *reply;
Packit 34410b
	GIOChannel *io;
Packit 34410b
	int fd;
Packit 34410b
Packit 34410b
	if (err != NULL) {
Packit 34410b
		reply = g_dbus_create_error(hdp_conn->msg,
Packit 34410b
						ERROR_INTERFACE ".HealthError",
Packit 34410b
						"%s", err->message);
Packit 34410b
		g_dbus_send_message(conn, reply);
Packit 34410b
Packit 34410b
		/* Send abort request because remote */
Packit 34410b
		/* side is now in PENDING state. */
Packit 34410b
		if (!mcap_mdl_abort(hdp_conn->hdp_chann->mdl,
Packit 34410b
					abort_echo_channel_cb,
Packit 34410b
					hdp_channel_ref(hdp_conn->hdp_chann),
Packit 34410b
					(GDestroyNotify) hdp_channel_unref,
Packit 34410b
					&gerr)) {
Packit 34410b
			error("%s", gerr->message);
Packit 34410b
			g_error_free(gerr);
Packit 34410b
			hdp_channel_unref(hdp_conn->hdp_chann);
Packit 34410b
		}
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	fd = mcap_mdl_get_fd(hdp_conn->hdp_chann->mdl);
Packit 34410b
	if (fd < 0) {
Packit 34410b
		reply = g_dbus_create_error(hdp_conn->msg,
Packit 34410b
						ERROR_INTERFACE ".HealthError",
Packit 34410b
						"Can't write in echo channel");
Packit 34410b
		g_dbus_send_message(conn, reply);
Packit 34410b
		delete_echo_channel(hdp_conn->hdp_chann);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	edata = hdp_conn->hdp_chann->edata;
Packit 34410b
	edata->buf = generate_echo_packet();
Packit 34410b
	send_echo_data(fd, edata->buf, HDP_ECHO_LEN);
Packit 34410b
Packit 34410b
	io = g_io_channel_unix_new(fd);
Packit 34410b
	g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN,
Packit 34410b
			check_echo, hdp_tmp_dc_data_ref(hdp_conn));
Packit 34410b
Packit 34410b
	edata->tid = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
Packit 34410b
					ECHO_TIMEOUT, echo_timeout,
Packit 34410b
					hdp_channel_ref(hdp_conn->hdp_chann),
Packit 34410b
					(GDestroyNotify) hdp_channel_unref);
Packit 34410b
Packit 34410b
	g_io_channel_unref(io);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void delete_mdl_cb(GError *err, gpointer data)
Packit 34410b
{
Packit 34410b
	if (err != NULL)
Packit 34410b
		error("Deleting error: %s", err->message);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void abort_and_del_mdl_cb(GError *err, gpointer data)
Packit 34410b
{
Packit 34410b
	struct mcap_mdl *mdl = data;
Packit 34410b
	GError *gerr = NULL;
Packit 34410b
Packit 34410b
	if (err != NULL) {
Packit 34410b
		error("%s", err->message);
Packit 34410b
		if (err->code == MCAP_INVALID_MDL) {
Packit 34410b
			/* MDL is removed from MCAP so we don't */
Packit 34410b
			/* need to delete it. */
Packit 34410b
			return;
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (!mcap_delete_mdl(mdl, delete_mdl_cb, NULL, NULL, &gerr)) {
Packit 34410b
		error("%s", gerr->message);
Packit 34410b
		g_error_free(gerr);
Packit 34410b
	}
Packit 34410b
}
Packit 34410b
Packit 34410b
static void abort_mdl_connection_cb(GError *err, gpointer data)
Packit 34410b
{
Packit 34410b
	struct hdp_tmp_dc_data *hdp_conn = data;
Packit 34410b
	struct hdp_channel *hdp_chann = hdp_conn->hdp_chann;
Packit 34410b
Packit 34410b
	if (err != NULL)
Packit 34410b
		error("Aborting error: %s", err->message);
Packit 34410b
Packit 34410b
	/* Connection operation has failed but we have to */
Packit 34410b
	/* notify the channel created at MCAP level */
Packit 34410b
	if (hdp_chann->mdep != HDP_MDEP_ECHO)
Packit 34410b
		g_dbus_emit_signal(btd_get_dbus_connection(),
Packit 34410b
					device_get_path(hdp_chann->dev->dev),
Packit 34410b
					HEALTH_DEVICE, "ChannelConnected",
Packit 34410b
					DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
Packit 34410b
					DBUS_TYPE_INVALID);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_mdl_conn_cb(struct mcap_mdl *mdl, GError *err, gpointer data)
Packit 34410b
{
Packit 34410b
	DBusConnection *conn = btd_get_dbus_connection();
Packit 34410b
	struct hdp_tmp_dc_data *hdp_conn =  data;
Packit 34410b
	struct hdp_channel *hdp_chann = hdp_conn->hdp_chann;
Packit 34410b
	struct hdp_device *dev = hdp_chann->dev;
Packit 34410b
	DBusMessage *reply;
Packit 34410b
	GError *gerr = NULL;
Packit 34410b
Packit 34410b
	if (err != NULL) {
Packit 34410b
		error("%s", err->message);
Packit 34410b
		reply = g_dbus_create_reply(hdp_conn->msg,
Packit 34410b
					DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
Packit 34410b
					DBUS_TYPE_INVALID);
Packit 34410b
		g_dbus_send_message(conn, reply);
Packit 34410b
Packit 34410b
		/* Send abort request because remote side */
Packit 34410b
		/* is now in PENDING state */
Packit 34410b
		if (!mcap_mdl_abort(hdp_chann->mdl, abort_mdl_connection_cb,
Packit 34410b
					hdp_tmp_dc_data_ref(hdp_conn),
Packit 34410b
					hdp_tmp_dc_data_destroy, &gerr)) {
Packit 34410b
			hdp_tmp_dc_data_unref(hdp_conn);
Packit 34410b
			error("%s", gerr->message);
Packit 34410b
			g_error_free(gerr);
Packit 34410b
		}
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	reply = g_dbus_create_reply(hdp_conn->msg,
Packit 34410b
					DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
Packit 34410b
					DBUS_TYPE_INVALID);
Packit 34410b
	g_dbus_send_message(conn, reply);
Packit 34410b
Packit 34410b
	g_dbus_emit_signal(conn, device_get_path(hdp_chann->dev->dev),
Packit 34410b
				HEALTH_DEVICE, "ChannelConnected",
Packit 34410b
				DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
Packit 34410b
				DBUS_TYPE_INVALID);
Packit 34410b
Packit 34410b
	if (!check_channel_conf(hdp_chann)) {
Packit 34410b
		close_mdl(hdp_chann);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (dev->fr != NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	dev->fr = hdp_channel_ref(hdp_chann);
Packit 34410b
Packit 34410b
	g_dbus_emit_property_changed(btd_get_dbus_connection(),
Packit 34410b
				device_get_path(dev->dev), HEALTH_DEVICE,
Packit 34410b
				"MainChannel");
Packit 34410b
}
Packit 34410b
Packit 34410b
static void device_create_mdl_cb(struct mcap_mdl *mdl, uint8_t conf,
Packit 34410b
						GError *err, gpointer data)
Packit 34410b
{
Packit 34410b
	DBusConnection *conn = btd_get_dbus_connection();
Packit 34410b
	struct hdp_create_dc *user_data = data;
Packit 34410b
	struct hdp_tmp_dc_data *hdp_conn;
Packit 34410b
	struct hdp_channel *hdp_chan;
Packit 34410b
	GError *gerr = NULL;
Packit 34410b
	DBusMessage *reply;
Packit 34410b
Packit 34410b
	if (err != NULL) {
Packit 34410b
		reply = g_dbus_create_error(user_data->msg,
Packit 34410b
					ERROR_INTERFACE ".HealthError",
Packit 34410b
					"%s", err->message);
Packit 34410b
		g_dbus_send_message(conn, reply);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (user_data->mdep != HDP_MDEP_ECHO &&
Packit 34410b
				user_data->config == HDP_NO_PREFERENCE_DC) {
Packit 34410b
		if (user_data->dev->fr == NULL && conf != HDP_RELIABLE_DC) {
Packit 34410b
			g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
Packit 34410b
					"Data channel aborted, first data "
Packit 34410b
					"channel should be reliable");
Packit 34410b
			goto fail;
Packit 34410b
		} else if (conf == HDP_NO_PREFERENCE_DC ||
Packit 34410b
						conf > HDP_STREAMING_DC) {
Packit 34410b
			g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
Packit 34410b
							"Data channel aborted, "
Packit 34410b
							"configuration error");
Packit 34410b
			goto fail;
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	hdp_chan = create_channel(user_data->dev, conf, mdl,
Packit 34410b
							mcap_mdl_get_mdlid(mdl),
Packit 34410b
							user_data->app, &gerr);
Packit 34410b
	if (hdp_chan == NULL)
Packit 34410b
		goto fail;
Packit 34410b
Packit 34410b
	hdp_conn = g_new0(struct hdp_tmp_dc_data, 1);
Packit 34410b
	hdp_conn->msg = dbus_message_ref(user_data->msg);
Packit 34410b
	hdp_conn->hdp_chann = hdp_chan;
Packit 34410b
	hdp_conn->cb = user_data->cb;
Packit 34410b
	hdp_chan->mdep = user_data->mdep;
Packit 34410b
Packit 34410b
	if (hdp_get_dcpsm(hdp_chan->dev, hdp_get_dcpsm_cb,
Packit 34410b
						hdp_tmp_dc_data_ref(hdp_conn),
Packit 34410b
						hdp_tmp_dc_data_destroy, &gerr))
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	error("%s", gerr->message);
Packit 34410b
	g_error_free(gerr);
Packit 34410b
Packit 34410b
	reply = g_dbus_create_reply(hdp_conn->msg,
Packit 34410b
					DBUS_TYPE_OBJECT_PATH, &hdp_chan->path,
Packit 34410b
					DBUS_TYPE_INVALID);
Packit 34410b
	g_dbus_send_message(conn, reply);
Packit 34410b
	hdp_tmp_dc_data_unref(hdp_conn);
Packit 34410b
Packit 34410b
	/* Send abort request because remote side is now in PENDING state */
Packit 34410b
	if (!mcap_mdl_abort(hdp_chan->mdl, abort_mdl_connection_cb,
Packit 34410b
					hdp_tmp_dc_data_ref(hdp_conn),
Packit 34410b
					hdp_tmp_dc_data_destroy, &gerr)) {
Packit 34410b
		hdp_tmp_dc_data_unref(hdp_conn);
Packit 34410b
		error("%s", gerr->message);
Packit 34410b
		g_error_free(gerr);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	return;
Packit 34410b
Packit 34410b
fail:
Packit 34410b
	reply = g_dbus_create_error(user_data->msg,
Packit 34410b
						ERROR_INTERFACE ".HealthError",
Packit 34410b
						"%s", gerr->message);
Packit 34410b
	g_dbus_send_message(conn, reply);
Packit 34410b
	g_error_free(gerr);
Packit 34410b
Packit 34410b
	/* Send abort request because remote side is now in PENDING */
Packit 34410b
	/* state. Then we have to delete it because we couldn't */
Packit 34410b
	/* register the HealthChannel interface */
Packit 34410b
	if (!mcap_mdl_abort(mdl, abort_and_del_mdl_cb, mcap_mdl_ref(mdl),
Packit 34410b
				(GDestroyNotify) mcap_mdl_unref, &gerr)) {
Packit 34410b
		error("%s", gerr->message);
Packit 34410b
		g_error_free(gerr);
Packit 34410b
		mcap_mdl_unref(mdl);
Packit 34410b
	}
Packit 34410b
}
Packit 34410b
Packit 34410b
static void device_create_dc_cb(gpointer user_data, GError *err)
Packit 34410b
{
Packit 34410b
	DBusConnection *conn = btd_get_dbus_connection();
Packit 34410b
	struct hdp_create_dc *data = user_data;
Packit 34410b
	DBusMessage *reply;
Packit 34410b
	GError *gerr = NULL;
Packit 34410b
Packit 34410b
	if (err != NULL) {
Packit 34410b
		reply = g_dbus_create_error(data->msg,
Packit 34410b
					ERROR_INTERFACE ".HealthError",
Packit 34410b
					"%s", err->message);
Packit 34410b
		g_dbus_send_message(conn, reply);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (data->dev->mcl == NULL) {
Packit 34410b
		g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
Packit 34410b
				"Mcl was closed");
Packit 34410b
		goto fail;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	hdp_create_data_ref(data);
Packit 34410b
Packit 34410b
	if (mcap_create_mdl(data->dev->mcl, data->mdep, data->config,
Packit 34410b
						device_create_mdl_cb, data,
Packit 34410b
						destroy_create_dc_data, &gerr))
Packit 34410b
		return;
Packit 34410b
	hdp_create_data_unref(data);
Packit 34410b
Packit 34410b
fail:
Packit 34410b
	reply = g_dbus_create_error(data->msg, ERROR_INTERFACE ".HealthError",
Packit 34410b
							"%s", gerr->message);
Packit 34410b
	g_error_free(gerr);
Packit 34410b
	g_dbus_send_message(conn, reply);
Packit 34410b
}
Packit 34410b
Packit 34410b
static DBusMessage *device_echo(DBusConnection *conn,
Packit 34410b
					DBusMessage *msg, void *user_data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *device = user_data;
Packit 34410b
	struct hdp_create_dc *data;
Packit 34410b
	DBusMessage *reply;
Packit 34410b
	GError *err = NULL;
Packit 34410b
Packit 34410b
	data = g_new0(struct hdp_create_dc, 1);
Packit 34410b
	data->dev = health_device_ref(device);
Packit 34410b
	data->mdep = HDP_MDEP_ECHO;
Packit 34410b
	data->config = HDP_RELIABLE_DC;
Packit 34410b
	data->msg = dbus_message_ref(msg);
Packit 34410b
	data->cb = hdp_echo_connect_cb;
Packit 34410b
	hdp_create_data_ref(data);
Packit 34410b
Packit 34410b
	if (device->mcl_conn && device->mcl) {
Packit 34410b
		if (mcap_create_mdl(device->mcl, data->mdep, data->config,
Packit 34410b
						device_create_mdl_cb, data,
Packit 34410b
						destroy_create_dc_data, &err))
Packit 34410b
			return NULL;
Packit 34410b
		goto fail;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (hdp_establish_mcl(data->dev, device_create_dc_cb,
Packit 34410b
					data, destroy_create_dc_data, &err))
Packit 34410b
		return NULL;
Packit 34410b
Packit 34410b
fail:
Packit 34410b
	reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
Packit 34410b
							"%s", err->message);
Packit 34410b
	g_error_free(err);
Packit 34410b
	hdp_create_data_unref(data);
Packit 34410b
	return reply;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void device_get_mdep_cb(uint8_t mdep, gpointer data, GError *err)
Packit 34410b
{
Packit 34410b
	DBusConnection *conn = btd_get_dbus_connection();
Packit 34410b
	struct hdp_create_dc *dc_data, *user_data = data;
Packit 34410b
	DBusMessage *reply;
Packit 34410b
	GError *gerr = NULL;
Packit 34410b
Packit 34410b
	if (err != NULL) {
Packit 34410b
		reply = g_dbus_create_error(user_data->msg,
Packit 34410b
						ERROR_INTERFACE ".HealthError",
Packit 34410b
						"%s", err->message);
Packit 34410b
		g_dbus_send_message(conn, reply);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	dc_data = hdp_create_data_ref(user_data);
Packit 34410b
	dc_data->mdep = mdep;
Packit 34410b
Packit 34410b
	if (user_data->dev->mcl_conn) {
Packit 34410b
		device_create_dc_cb(dc_data, NULL);
Packit 34410b
		hdp_create_data_unref(dc_data);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (hdp_establish_mcl(dc_data->dev, device_create_dc_cb,
Packit 34410b
					dc_data, destroy_create_dc_data, &gerr))
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	reply = g_dbus_create_error(user_data->msg,
Packit 34410b
						ERROR_INTERFACE ".HealthError",
Packit 34410b
						"%s", gerr->message);
Packit 34410b
	hdp_create_data_unref(dc_data);
Packit 34410b
	g_error_free(gerr);
Packit 34410b
	g_dbus_send_message(conn, reply);
Packit 34410b
}
Packit 34410b
Packit 34410b
static DBusMessage *device_create_channel(DBusConnection *conn,
Packit 34410b
					DBusMessage *msg, void *user_data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *device = user_data;
Packit 34410b
	struct hdp_application *app;
Packit 34410b
	struct hdp_create_dc *data;
Packit 34410b
	char *app_path, *conf;
Packit 34410b
	DBusMessage *reply;
Packit 34410b
	GError *err = NULL;
Packit 34410b
	uint8_t config;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &app_path,
Packit 34410b
							DBUS_TYPE_STRING, &conf,
Packit 34410b
							DBUS_TYPE_INVALID))
Packit 34410b
		return btd_error_invalid_args(msg);
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(applications, app_path, cmp_app);
Packit 34410b
	if (l == NULL)
Packit 34410b
		return btd_error_invalid_args(msg);
Packit 34410b
Packit 34410b
	app = l->data;
Packit 34410b
Packit 34410b
	if (g_ascii_strcasecmp("reliable", conf) == 0)
Packit 34410b
		config = HDP_RELIABLE_DC;
Packit 34410b
	else if (g_ascii_strcasecmp("streaming", conf) == 0)
Packit 34410b
		config = HDP_STREAMING_DC;
Packit 34410b
	else if (g_ascii_strcasecmp("any", conf) == 0)
Packit 34410b
		config = HDP_NO_PREFERENCE_DC;
Packit 34410b
	else
Packit 34410b
		return btd_error_invalid_args(msg);
Packit 34410b
Packit 34410b
	if (app->role == HDP_SINK && config != HDP_NO_PREFERENCE_DC)
Packit 34410b
		return btd_error_invalid_args(msg);
Packit 34410b
Packit 34410b
	if (app->role == HDP_SOURCE && config == HDP_NO_PREFERENCE_DC)
Packit 34410b
		return btd_error_invalid_args(msg);
Packit 34410b
Packit 34410b
	if (!device->fr && config == HDP_STREAMING_DC)
Packit 34410b
		return btd_error_invalid_args(msg);
Packit 34410b
Packit 34410b
	data = g_new0(struct hdp_create_dc, 1);
Packit 34410b
	data->dev = health_device_ref(device);
Packit 34410b
	data->config = config;
Packit 34410b
	data->app = hdp_application_ref(app);
Packit 34410b
	data->msg = dbus_message_ref(msg);
Packit 34410b
	data->cb = hdp_mdl_conn_cb;
Packit 34410b
Packit 34410b
	if (hdp_get_mdep(device, l->data, device_get_mdep_cb,
Packit 34410b
						hdp_create_data_ref(data),
Packit 34410b
						destroy_create_dc_data, &err))
Packit 34410b
		return NULL;
Packit 34410b
Packit 34410b
	reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
Packit 34410b
							"%s", err->message);
Packit 34410b
	g_error_free(err);
Packit 34410b
	hdp_create_data_unref(data);
Packit 34410b
	return reply;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_mdl_delete_cb(GError *err, gpointer data)
Packit 34410b
{
Packit 34410b
	DBusConnection *conn = btd_get_dbus_connection();
Packit 34410b
	struct hdp_tmp_dc_data *del_data = data;
Packit 34410b
	DBusMessage *reply;
Packit 34410b
	char *path;
Packit 34410b
Packit 34410b
	if (err != NULL && err->code != MCAP_INVALID_MDL) {
Packit 34410b
		reply = g_dbus_create_error(del_data->msg,
Packit 34410b
						ERROR_INTERFACE ".HealthError",
Packit 34410b
						"%s", err->message);
Packit 34410b
		g_dbus_send_message(conn, reply);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	path = g_strdup(del_data->hdp_chann->path);
Packit 34410b
	g_dbus_unregister_interface(conn, path, HEALTH_CHANNEL);
Packit 34410b
	g_free(path);
Packit 34410b
Packit 34410b
	reply = g_dbus_create_reply(del_data->msg, DBUS_TYPE_INVALID);
Packit 34410b
	g_dbus_send_message(conn, reply);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void hdp_continue_del_cb(gpointer user_data, GError *err)
Packit 34410b
{
Packit 34410b
	DBusConnection *conn = btd_get_dbus_connection();
Packit 34410b
	struct hdp_tmp_dc_data *del_data = user_data;
Packit 34410b
	GError *gerr = NULL;
Packit 34410b
	DBusMessage *reply;
Packit 34410b
Packit 34410b
	if (err != NULL) {
Packit 34410b
		reply = g_dbus_create_error(del_data->msg,
Packit 34410b
					ERROR_INTERFACE ".HealthError",
Packit 34410b
					"%s", err->message);
Packit 34410b
		g_dbus_send_message(conn, reply);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (mcap_delete_mdl(del_data->hdp_chann->mdl, hdp_mdl_delete_cb,
Packit 34410b
						hdp_tmp_dc_data_ref(del_data),
Packit 34410b
						hdp_tmp_dc_data_destroy, &gerr))
Packit 34410b
			return;
Packit 34410b
Packit 34410b
	reply = g_dbus_create_error(del_data->msg,
Packit 34410b
						ERROR_INTERFACE ".HealthError",
Packit 34410b
						"%s", gerr->message);
Packit 34410b
	hdp_tmp_dc_data_unref(del_data);
Packit 34410b
	g_error_free(gerr);
Packit 34410b
	g_dbus_send_message(conn, reply);
Packit 34410b
}
Packit 34410b
Packit 34410b
static DBusMessage *device_destroy_channel(DBusConnection *conn,
Packit 34410b
					DBusMessage *msg, void *user_data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *device = user_data;
Packit 34410b
	struct hdp_tmp_dc_data *del_data;
Packit 34410b
	struct hdp_channel *hdp_chan;
Packit 34410b
	DBusMessage *reply;
Packit 34410b
	GError *err = NULL;
Packit 34410b
	char *path;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
Packit 34410b
							DBUS_TYPE_INVALID)){
Packit 34410b
		return btd_error_invalid_args(msg);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(device->channels, path, cmp_chan_path);
Packit 34410b
	if (l == NULL)
Packit 34410b
		return btd_error_invalid_args(msg);
Packit 34410b
Packit 34410b
	hdp_chan = l->data;
Packit 34410b
	del_data = g_new0(struct hdp_tmp_dc_data, 1);
Packit 34410b
	del_data->msg = dbus_message_ref(msg);
Packit 34410b
	del_data->hdp_chann = hdp_channel_ref(hdp_chan);
Packit 34410b
Packit 34410b
	if (device->mcl_conn) {
Packit 34410b
		if (mcap_delete_mdl(hdp_chan->mdl, hdp_mdl_delete_cb,
Packit 34410b
						hdp_tmp_dc_data_ref(del_data),
Packit 34410b
						hdp_tmp_dc_data_destroy, &err))
Packit 34410b
			return NULL;
Packit 34410b
		goto fail;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (hdp_establish_mcl(device, hdp_continue_del_cb,
Packit 34410b
						hdp_tmp_dc_data_ref(del_data),
Packit 34410b
						hdp_tmp_dc_data_destroy, &err))
Packit 34410b
		return NULL;
Packit 34410b
Packit 34410b
fail:
Packit 34410b
	reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
Packit 34410b
							"%s", err->message);
Packit 34410b
	hdp_tmp_dc_data_unref(del_data);
Packit 34410b
	g_error_free(err);
Packit 34410b
	return reply;
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean dev_property_exists_main_channel(
Packit 34410b
				const GDBusPropertyTable *property, void *data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *device = data;
Packit 34410b
	return device->fr != NULL;
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean dev_property_get_main_channel(
Packit 34410b
					const GDBusPropertyTable *property,
Packit 34410b
					DBusMessageIter *iter, void *data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *device = data;
Packit 34410b
Packit 34410b
	if (device->fr == NULL)
Packit 34410b
		return FALSE;
Packit 34410b
Packit 34410b
	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
Packit 34410b
							&device->fr->path);
Packit 34410b
Packit 34410b
	return TRUE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void health_device_destroy(void *data)
Packit 34410b
{
Packit 34410b
	struct hdp_device *device = data;
Packit 34410b
Packit 34410b
	DBG("Unregistered interface %s on path %s", HEALTH_DEVICE,
Packit 34410b
						device_get_path(device->dev));
Packit 34410b
Packit 34410b
	remove_channels(device);
Packit 34410b
	if (device->ndc != NULL) {
Packit 34410b
		hdp_channel_unref(device->ndc);
Packit 34410b
		device->ndc = NULL;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	devices = g_slist_remove(devices, device);
Packit 34410b
	health_device_unref(device);
Packit 34410b
}
Packit 34410b
Packit 34410b
static const GDBusMethodTable health_device_methods[] = {
Packit 34410b
	{ GDBUS_ASYNC_METHOD("Echo",
Packit 34410b
			NULL, GDBUS_ARGS({ "value", "b" }), device_echo) },
Packit 34410b
	{ GDBUS_ASYNC_METHOD("CreateChannel",
Packit 34410b
			GDBUS_ARGS({ "application", "o" },
Packit 34410b
					{ "configuration", "s" }),
Packit 34410b
			GDBUS_ARGS({ "channel", "o" }),
Packit 34410b
			device_create_channel) },
Packit 34410b
	{ GDBUS_ASYNC_METHOD("DestroyChannel",
Packit 34410b
			GDBUS_ARGS({ "channel", "o" }), NULL,
Packit 34410b
			device_destroy_channel) },
Packit 34410b
	{ }
Packit 34410b
};
Packit 34410b
Packit 34410b
static const GDBusSignalTable health_device_signals[] = {
Packit 34410b
	{ GDBUS_SIGNAL("ChannelConnected",
Packit 34410b
			GDBUS_ARGS({ "channel", "o" })) },
Packit 34410b
	{ GDBUS_SIGNAL("ChannelDeleted",
Packit 34410b
			GDBUS_ARGS({ "channel", "o" })) },
Packit 34410b
	{ }
Packit 34410b
};
Packit 34410b
Packit 34410b
static const GDBusPropertyTable health_device_properties[] = {
Packit 34410b
	{ "MainChannel", "o", dev_property_get_main_channel, NULL,
Packit 34410b
					dev_property_exists_main_channel },
Packit 34410b
	{ }
Packit 34410b
};
Packit 34410b
Packit 34410b
static struct hdp_device *create_health_device(struct btd_device *device)
Packit 34410b
{
Packit 34410b
	struct btd_adapter *adapter = device_get_adapter(device);
Packit 34410b
	const char *path = device_get_path(device);
Packit 34410b
	struct hdp_device *dev;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	if (device == NULL)
Packit 34410b
		return NULL;
Packit 34410b
Packit 34410b
	dev = g_new0(struct hdp_device, 1);
Packit 34410b
	dev->dev = btd_device_ref(device);
Packit 34410b
	health_device_ref(dev);
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(adapters, adapter, cmp_adapter);
Packit 34410b
	if (l == NULL)
Packit 34410b
		goto fail;
Packit 34410b
Packit 34410b
	dev->hdp_adapter = l->data;
Packit 34410b
Packit 34410b
	if (!g_dbus_register_interface(btd_get_dbus_connection(),
Packit 34410b
					path, HEALTH_DEVICE,
Packit 34410b
					health_device_methods,
Packit 34410b
					health_device_signals,
Packit 34410b
					health_device_properties,
Packit 34410b
					dev, health_device_destroy)) {
Packit 34410b
		error("D-Bus failed to register %s interface", HEALTH_DEVICE);
Packit 34410b
		goto fail;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	DBG("Registered interface %s on path %s", HEALTH_DEVICE, path);
Packit 34410b
	return dev;
Packit 34410b
Packit 34410b
fail:
Packit 34410b
	health_device_unref(dev);
Packit 34410b
	return NULL;
Packit 34410b
}
Packit 34410b
Packit 34410b
int hdp_device_register(struct btd_device *device)
Packit 34410b
{
Packit 34410b
	struct hdp_device *hdev;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(devices, device, cmp_device);
Packit 34410b
	if (l != NULL) {
Packit 34410b
		hdev = l->data;
Packit 34410b
		hdev->sdp_present = TRUE;
Packit 34410b
		return 0;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	hdev = create_health_device(device);
Packit 34410b
	if (hdev == NULL)
Packit 34410b
		return -1;
Packit 34410b
Packit 34410b
	hdev->sdp_present = TRUE;
Packit 34410b
Packit 34410b
	devices = g_slist_prepend(devices, hdev);
Packit 34410b
	return 0;
Packit 34410b
}
Packit 34410b
Packit 34410b
void hdp_device_unregister(struct btd_device *device)
Packit 34410b
{
Packit 34410b
	struct hdp_device *hdp_dev;
Packit 34410b
	const char *path;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	l = g_slist_find_custom(devices, device, cmp_device);
Packit 34410b
	if (l == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	hdp_dev = l->data;
Packit 34410b
	path = device_get_path(hdp_dev->dev);
Packit 34410b
	g_dbus_unregister_interface(btd_get_dbus_connection(),
Packit 34410b
							path, HEALTH_DEVICE);
Packit 34410b
}
Packit 34410b
Packit 34410b
int hdp_manager_start(void)
Packit 34410b
{
Packit 34410b
	DBG("Starting Health manager");
Packit 34410b
Packit 34410b
	if (!g_dbus_register_interface(btd_get_dbus_connection(),
Packit 34410b
					MANAGER_PATH, HEALTH_MANAGER,
Packit 34410b
					health_manager_methods, NULL, NULL,
Packit 34410b
					NULL, manager_path_unregister)) {
Packit 34410b
		error("D-Bus failed to register %s interface", HEALTH_MANAGER);
Packit 34410b
		return -1;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	return 0;
Packit 34410b
}
Packit 34410b
Packit 34410b
void hdp_manager_stop(void)
Packit 34410b
{
Packit 34410b
	g_dbus_unregister_interface(btd_get_dbus_connection(),
Packit 34410b
						MANAGER_PATH, HEALTH_MANAGER);
Packit 34410b
Packit 34410b
	DBG("Stopped Health manager");
Packit 34410b
}
Packit 34410b
Packit 34410b
struct hdp_device *health_device_ref(struct hdp_device *hdp_dev)
Packit 34410b
{
Packit 34410b
	hdp_dev->ref++;
Packit 34410b
Packit 34410b
	DBG("(%p): ref=%d", hdp_dev, hdp_dev->ref);
Packit 34410b
Packit 34410b
	return hdp_dev;
Packit 34410b
}
Packit 34410b
Packit 34410b
void health_device_unref(struct hdp_device *hdp_dev)
Packit 34410b
{
Packit 34410b
	hdp_dev->ref--;
Packit 34410b
Packit 34410b
	DBG("(%p): ref=%d", hdp_dev, hdp_dev->ref);
Packit 34410b
Packit 34410b
	if (hdp_dev->ref > 0)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	free_health_device(hdp_dev);
Packit 34410b
}