Add reference counter to hdp_channel
diff --git a/health/hdp.c b/health/hdp.c
index 768d6df..b02d708 100644
--- a/health/hdp.c
+++ b/health/hdp.c
@@ -60,6 +60,7 @@
 static gboolean update_adapter(struct hdp_adapter *adapter);
 static struct hdp_device *create_health_device(DBusConnection *conn,
 						struct btd_device *device);
+static void free_echo_data(struct hdp_echo_data *edata);
 
 struct hdp_create_dc {
 	DBusConnection			*conn;
@@ -88,6 +89,42 @@
 	struct hdp_tmp_dc_data	*hdp_conn;	/* temporal data */
 };
 
+static struct hdp_channel *hdp_channel_ref(struct hdp_channel *chan)
+{
+	if (!chan)
+		return NULL;
+
+	chan->ref++;
+
+	DBG("health_channel_ref(%p): ref=%d", chan, chan->ref);
+	return chan;
+}
+
+static void free_health_channel(struct hdp_channel *chan)
+{
+	if (chan->mdep == HDP_MDEP_ECHO)
+		free_echo_data(chan->edata);
+
+	hdp_application_unref(chan->app);
+	health_device_unref(chan->dev);
+	g_free(chan->path);
+	g_free(chan);
+}
+
+static void hdp_channel_unref(struct hdp_channel *chan)
+{
+	if (!chan)
+		return;
+
+	chan->ref --;
+	DBG("health_channel_unref(%p): ref=%d", chan, chan->ref);
+
+	if (chan->ref > 0)
+		return;
+
+	free_health_channel(chan);
+}
+
 static void free_hdp_create_dc(struct hdp_create_dc *dc_data)
 {
 	dbus_message_unref(dc_data->msg);
@@ -123,6 +160,7 @@
 {
 	dbus_message_unref(data->msg);
 	dbus_connection_unref(data->conn);
+	hdp_channel_unref(data->hdp_chann);
 
 	g_free(data);
 }
@@ -470,7 +508,7 @@
 
 		/* Send abort request because remote side */
 		/* is now in PENDING state */
-		if (!mcap_mdl_abort(chan->mdl, abort_mdl_cb, chan, NULL,
+		if (!mcap_mdl_abort(chan->mdl, abort_mdl_cb, NULL, NULL,
 								&gerr)) {
 			error("%s", gerr->message);
 			g_error_free(gerr);
@@ -550,7 +588,7 @@
 
 	/* Send abort request because remote side is now in PENDING state */
 	if (!mcap_mdl_abort(dc_data->hdp_chann->mdl, abort_mdl_cb,
-					dc_data->hdp_chann, NULL, &gerr)) {
+					NULL, NULL, &gerr)) {
 		error("%s", gerr->message);
 		g_error_free(gerr);
 	}
@@ -609,7 +647,7 @@
 	dc_data = g_new0(struct hdp_tmp_dc_data, 1);
 	dc_data->conn = dbus_connection_ref(conn);
 	dc_data->msg = dbus_message_ref(msg);
-	dc_data->hdp_chann = chan;
+	dc_data->hdp_chann = hdp_channel_ref(chan);
 
 	if (chan->dev->mcl_conn) {
 		reply = channel_acquire_continue(hdp_tmp_dc_data_ref(dc_data),
@@ -679,6 +717,9 @@
 	struct hdp_device *dev = hdp_chan->dev;
 
 	DBG("Destroy Health Channel %s", hdp_chan->path);
+	if (!g_slist_find(dev->channels, hdp_chan))
+		goto end;
+
 	dev->channels = g_slist_remove(dev->channels, hdp_chan);
 
 	if (hdp_chan->mdep != HDP_MDEP_ECHO)
@@ -690,6 +731,7 @@
 	if (hdp_chan == dev->fr) {
 		char *empty_path;
 
+		hdp_channel_unref(dev->fr);
 		dev->fr = NULL;
 		empty_path = "/";
 		emit_property_changed(dev->conn, device_get_path(dev->dev),
@@ -697,13 +739,8 @@
 					DBUS_TYPE_OBJECT_PATH, &empty_path);
 	}
 
-	if (hdp_chan->mdep == HDP_MDEP_ECHO)
-		free_echo_data(hdp_chan->edata);
-
-	hdp_application_unref(hdp_chan->app);
-	health_device_unref(hdp_chan->dev);
-	g_free(hdp_chan->path);
-	g_free(hdp_chan);
+end:
+	hdp_channel_unref(hdp_chan);
 }
 
 static GDBusMethodTable health_channels_methods[] = {
@@ -739,10 +776,11 @@
 					device_get_path(hdp_chann->dev->dev),
 					hdp_chann->mdlid);
 
-	dev->channels = g_slist_append(dev->channels, hdp_chann);
+	dev->channels = g_slist_append(dev->channels,
+						hdp_channel_ref(hdp_chann));
 
 	if (hdp_chann->mdep == HDP_MDEP_ECHO)
-		return hdp_chann;
+		return hdp_channel_ref(hdp_chann);
 
 	if (!g_dbus_register_interface(dev->conn, hdp_chann->path,
 					HEALTH_CHANNEL,
@@ -754,7 +792,7 @@
 		return NULL;
 	}
 
-	return hdp_chann;
+	return hdp_channel_ref(hdp_chann);
 }
 
 static void remove_channels(struct hdp_device *dev)
@@ -816,6 +854,7 @@
 
 	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
 		chan->edata->wid = 0;
+		hdp_channel_unref(chan);
 		return FALSE;
 	}
 
@@ -833,6 +872,7 @@
 fail:
 	close_device_con(chan->dev, FALSE);
 	chan->edata->wid = 0;
+	hdp_channel_unref(chan);
 	return FALSE;
 }
 
@@ -881,14 +921,14 @@
 
 	chan = dev->ndc;
 	chan->mdl = mdl;
-	dev->ndc = NULL;
 
 	if (!g_slist_find(dev->channels, chan))
-		dev->channels = g_slist_prepend(dev->channels, chan);
+		dev->channels = g_slist_prepend(dev->channels,
+							hdp_channel_ref(chan));
 
 	if (!check_channel_conf(chan)) {
 		close_mdl(chan);
-		return;
+		goto end;
 	}
 
 	if (chan->mdep == HDP_MDEP_ECHO) {
@@ -897,15 +937,15 @@
 
 		fd = mcap_mdl_get_fd(chan->mdl);
 		if (fd < 0)
-			return;
+			goto end;
 
 		chan->edata->echo_done = FALSE;
 		io = g_io_channel_unix_new(fd);
 		chan->edata->wid = g_io_add_watch(io,
 				G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN,
-				serve_echo, chan);
+				serve_echo, hdp_channel_ref(chan));
 		g_io_channel_unref(io);
-		return;
+		goto end;
 	}
 
 	g_dbus_emit_signal(dev->conn, device_get_path(dev->dev), HEALTH_DEVICE,
@@ -914,13 +954,17 @@
 					DBUS_TYPE_INVALID);
 
 	if (dev->fr)
-		return;
+		goto end;
 
-	dev->fr = chan;
+	dev->fr = hdp_channel_ref(chan);
 
 	emit_property_changed(dev->conn, device_get_path(dev->dev),
 					HEALTH_DEVICE, "MainChannel",
 					DBUS_TYPE_OBJECT_PATH, &dev->fr->path);
+
+end:
+	hdp_channel_unref(dev->ndc);
+	dev->ndc = NULL;
 }
 
 static void hdp_mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data)
@@ -963,7 +1007,8 @@
 	dev->ndc->mdl = mdl;
 
 	if (!g_slist_find(dev->channels, dev->ndc))
-		dev->channels = g_slist_prepend(dev->channels, dev->ndc);
+		dev->channels = g_slist_prepend(dev->channels,
+						hdp_channel_ref(dev->ndc));
 
 	if (dev->ndc->mdep != HDP_MDEP_ECHO)
 		g_dbus_emit_signal(dev->conn, device_get_path(dev->dev),
@@ -971,6 +1016,7 @@
 					DBUS_TYPE_OBJECT_PATH, &dev->ndc->path,
 					DBUS_TYPE_INVALID);
 
+	hdp_channel_unref(dev->ndc);
 	dev->ndc = NULL;
 }
 
@@ -1098,7 +1144,7 @@
 		return MCAP_MDL_BUSY;
 	}
 
-	dev->ndc = chan;
+	dev->ndc = hdp_channel_ref(chan);
 
 	return MCAP_SUCCESS;
 }
@@ -1358,10 +1404,12 @@
 		return;
 	}
 
-	if (mcap_delete_mdl(chan->mdl, delete_echo_channel_cb, chan, NULL,
-									&err))
+	if (mcap_delete_mdl(chan->mdl, delete_echo_channel_cb,
+				hdp_channel_ref(chan),
+				(GDestroyNotify) hdp_channel_unref, &err))
 		return;
 
+	hdp_channel_unref(chan);
 	error("Error deleting the echo channel: %s", err->message);
 	g_error_free(err);
 
@@ -1378,7 +1426,7 @@
 			/* MDL is removed from MCAP so we can */
 			/* free the data channel without sending */
 			/* a MD_DELETE_MDL_REQ */
-			health_channel_destroy(chan);
+			hdp_channel_unref(chan);
 		}
 		return;
 	}
@@ -1489,9 +1537,12 @@
 		/* side is now in PENDING state. */
 		if (!mcap_mdl_abort(hdp_conn->hdp_chann->mdl,
 					abort_echo_channel_cb,
-					hdp_conn->hdp_chann, NULL, &gerr)) {
+					hdp_channel_ref(hdp_conn->hdp_chann),
+					(GDestroyNotify) hdp_channel_unref,
+					&gerr)) {
 			error("%s", gerr->message);
 			g_error_free(gerr);
+			hdp_channel_unref(hdp_conn->hdp_chann);
 		}
 		return;
 	}
@@ -1542,7 +1593,7 @@
 		}
 	}
 
-	if (!mcap_delete_mdl(mdl, delete_mdl_cb, mdl, NULL, &gerr)) {
+	if (!mcap_delete_mdl(mdl, delete_mdl_cb, NULL, NULL, &gerr)) {
 		error("%s", gerr->message);
 		g_error_free(gerr);
 	}
@@ -1565,7 +1616,7 @@
 
 		/* Send abort request because remote side */
 		/* is now in PENDING state */
-		if (!mcap_mdl_abort(hdp_chann->mdl, abort_mdl_cb, hdp_chann,
+		if (!mcap_mdl_abort(hdp_chann->mdl, abort_mdl_cb, NULL,
 								NULL, &gerr)) {
 			error("%s", gerr->message);
 			g_error_free(gerr);
@@ -1586,7 +1637,7 @@
 	if (dev->fr)
 		return;
 
-	dev->fr = hdp_chann;
+	dev->fr = hdp_channel_ref(hdp_chann);
 
 	emit_property_changed(dev->conn, device_get_path(dev->dev),
 					HEALTH_DEVICE, "MainChannel",
@@ -1663,7 +1714,7 @@
 	hdp_tmp_dc_data_unref(hdp_conn);
 
 	/* Send abort request because remote side is now in PENDING state */
-	if (!mcap_mdl_abort(hdp_chan->mdl, abort_mdl_cb, hdp_chan, NULL,
+	if (!mcap_mdl_abort(hdp_chan->mdl, abort_mdl_cb, NULL, NULL,
 								&gerr)) {
 		error("%s", gerr->message);
 		g_error_free(gerr);
@@ -1940,7 +1991,7 @@
 	del_data = g_new0(struct hdp_tmp_dc_data, 1);
 	del_data->msg = dbus_message_ref(msg);
 	del_data->conn = dbus_connection_ref(conn);
-	del_data->hdp_chann = hdp_chan;
+	del_data->hdp_chann = hdp_channel_ref(hdp_chan);
 
 	if (device->mcl_conn) {
 		if (mcap_delete_mdl(hdp_chan->mdl, hdp_mdl_delete_cb,
@@ -2001,6 +2052,10 @@
 						device_get_path(device->dev));
 
 	remove_channels(device);
+	if (device->ndc) {
+		hdp_channel_unref(device->ndc);
+		device->ndc = NULL;
+	}
 
 	devices = g_slist_remove(devices, device);
 	health_device_unref(device);
diff --git a/health/hdp_types.h b/health/hdp_types.h
index 9c07c32..fffdb32 100644
--- a/health/hdp_types.h
+++ b/health/hdp_types.h
@@ -121,6 +121,7 @@
 	uint8_t			mdep;		/* Remote MDEP */
 	uint16_t		mdlid;		/* Data channel Id */
 	struct hdp_echo_data	*edata;		/* private data used by echo channels */
+	gint			ref;		/* Reference counter */
 };
 
 #endif /* __HDP_TYPES_H__ */