All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Multi-Channel Adaptation Protocol.
@ 2010-04-06 14:00 Santiago Carot Nemesio
  2010-04-09 11:44 ` [PATCH] Added support for deleting all MDLS in MCAP José Antonio Santos Cadenas
  0 siblings, 1 reply; 4+ messages in thread
From: Santiago Carot Nemesio @ 2010-04-06 14:00 UTC (permalink / raw)
  To: linux-bluetooth

Support for Standard OP. Codes.

Signed-off-by: Santiago Carot-Nemesio <sancane@gmail.com>
Reviewed-by: Jose Antonio Santos Cadenas <santoscadenas@gmail.com>
---
 Makefile.am     |   11 +-
 acinclude.m4    |    6 +
 mcap/mcap.c     | 1807 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 mcap/mcap.h     |  174 ++++++
 mcap/mcap_lib.h |  137 +++++
 5 files changed, 2134 insertions(+), 1 deletions(-)
 create mode 100644 mcap/mcap.c
 create mode 100644 mcap/mcap.h
 create mode 100644 mcap/mcap_lib.h

diff --git a/Makefile.am b/Makefile.am
index a9046db..3590cdb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -101,6 +101,7 @@ gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/object.c gdbus/watch.c
 builtin_modules =
 builtin_sources =
 builtin_nodist =
+mcap_sources =
 
 if PNATPLUGIN
 builtin_modules += pnat
@@ -168,6 +169,10 @@ builtin_modules += service
 builtin_sources += plugins/service.c
 endif
 
+if MCAP
+mcap_sources += mcap/mcap_lib.h mcap/mcap.h mcap/mcap.c
+endif
+
 builtin_modules += hciops
 builtin_sources += plugins/hciops.c
 
@@ -196,7 +201,8 @@ src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
 			src/adapter.h src/adapter.c \
 			src/device.h src/device.c \
 			src/dbus-common.c src/dbus-common.h \
-			src/dbus-hci.h src/dbus-hci.c
+			src/dbus-hci.h src/dbus-hci.c \
+			$(mcap_sources)
 src_bluetoothd_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @DBUS_LIBS@ \
 							@CAPNG_LIBS@ -ldl
 src_bluetoothd_LDFLAGS = -Wl,--export-dynamic \
@@ -318,6 +324,9 @@ AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ \
 INCLUDES = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
 			-I$(srcdir)/audio -I$(srcdir)/sbc -I$(srcdir)/gdbus
 
+if MCAP
+INCLUDES += -I$(builddir)/mcap
+endif
 
 pkgconfigdir = $(libdir)/pkgconfig
 
diff --git a/acinclude.m4 b/acinclude.m4
index f7bb047..b512cfb 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -167,6 +167,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	serial_enable=yes
 	network_enable=yes
 	service_enable=yes
+	mcap_enable=no
 	pnat_enable=no
 	tracer_enable=no
 	tools_enable=yes
@@ -215,6 +216,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 		service_enable=${enableval}
 	])
 
+	AC_ARG_ENABLE(mcap, AC_HELP_STRING([--enable-mcap], [enable mcap support]), [
+			mcap_enable=${enableval}
+	])
+
 	AC_ARG_ENABLE(pnat, AC_HELP_STRING([--enable-pnat], [enable pnat plugin]), [
 		pnat_enable=${enableval}
 	])
@@ -325,6 +330,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	AM_CONDITIONAL(SERIALPLUGIN, test "${serial_enable}" = "yes")
 	AM_CONDITIONAL(NETWORKPLUGIN, test "${network_enable}" = "yes")
 	AM_CONDITIONAL(SERVICEPLUGIN, test "${service_enable}" = "yes")
+	AM_CONDITIONAL(MCAP, test "${mcap_enable}" = "yes")
 	AM_CONDITIONAL(ECHOPLUGIN, test "no" = "yes")
 	AM_CONDITIONAL(PNATPLUGIN, test "${pnat_enable}" = "yes")
 	AM_CONDITIONAL(TRACER, test "${tracer_enable}" = "yes")
diff --git a/mcap/mcap.c b/mcap/mcap.c
new file mode 100644
index 0000000..28c586c
--- /dev/null
+++ b/mcap/mcap.c
@@ -0,0 +1,1807 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 Santiago Carot Nemesio <sancane at gmail.com>
+ *  Copyright (C) 2010 Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <gdbus.h>
+#include "adapter.h"
+#include "logging.h"
+#include "btio.h"
+#include "error.h"
+
+#include <netinet/in.h>
+
+#include "mcap.h"
+#include "mcap_lib.h"
+
+//#define STATE2STR(_mcl) state2str(_mcl->state)
+#define MCAP_ERROR mcap_error_quark()
+#define SET_DEFAULT_MCL_CB(__mcl) do {				\
+	__mcl->cb->mdl_connected = default_mdl_connected_cb;	\
+	__mcl->cb->mdl_closed = default_mdl_closed_cb;		\
+	__mcl->cb->mdl_deleted = default_mdl_deleted_cb;	\
+	__mcl->cb->mdl_conn_req = default_mdl_conn_req_cb;	\
+	__mcl->cb->mdl_reconn_req = default_mdl_reconn_req_cb;	\
+} while(0)
+#define RELEASE_TIMER(__mcl) do {	\
+	g_source_remove(__mcl->tid);	\
+	__mcl->tid = 0;			\
+} while(0)
+
+#define RESPONSE_TIMER 4 /* seconds */
+
+#define MCAP_IS_STD_OPCODE(_oc)	(_oc >= MCAP_ERROR_RSP &&	\
+				 _oc <= MCAP_MD_DELETE_MDL_RSP)
+
+typedef enum {
+	MCL_CONNECTED,
+	MCL_PENDING,
+	MCL_ACTIVE
+} MCLState;
+
+typedef enum {
+	MCL_ACCEPTOR,
+	MCL_INITIATOR,
+} MCLRole;
+
+typedef enum {
+	MCL_AVAILABLE,
+	MCL_WAITING_RSP,
+} MCAPCtrl;
+
+typedef enum {
+	MDL_WAITING,
+	MDL_CONNECTED,
+	MDL_CLOSED
+} MDLState;
+
+struct mcap_mcl_cb {
+	mcap_mdl_event_cb 		mdl_connected;	/* Remote device has created an mdl */
+	mcap_mdl_event_cb 		mdl_closed;	/* Remote device has closed an mdl */
+	mcap_mdl_event_cb 		mdl_deleted;	/* Remote device deleted an mdl */
+	mcap_remote_mdl_conn_req_cb	mdl_conn_req;	/* Remote deive requested create an mdl */
+	mcap_remote_mdl_reconn_req_cb 	mdl_reconn_req;	/* Remote device requested reconnect previus mdl */
+	gpointer			user_data;	/* user data */
+};
+
+struct mcap_session {
+	bdaddr_t		src;		/* Source address */
+	GIOChannel		*ccio;		/* Control Channel IO */
+	GIOChannel		*dcio;		/* Data Channel IO */
+	GSList			*mcls;		/* MCAP session list */
+	BtIOSecLevel		sec;		/* Security level */
+	mcap_mcl_event_cb	mcl_closed_cb;	/* Mcl closed callback */
+	mcap_mcl_event_cb	mcl_created_cb;	/* New Mcl createc callback */
+	gpointer		user_data;	/* User data for callbacks */
+};
+
+struct mcap_mcl {
+	struct mcap_session	*ms;		/* MCAP session where this MCL belongs */
+	bdaddr_t		addr;		/* device address */
+	GIOChannel		*cc;		/* MCAP Control Channel IO */
+	guint			wid;		/* MCL Watcher id */
+	GSList			*mdls;		/* List of Data Channels shorted by mdlid */
+	MCLState		state;		/* mcap state */
+	MCLRole			role;		/* initiator or aceptor of this MCL*/
+	MCAPCtrl		req;		/* Request control flag */
+	void			*priv_data;	/* Temporal data to manage responses */
+	struct mcap_mcl_cb	*cb;		/* MCL callbacks */
+	guint			tid;		/* Timer id for waiting for a resposne */
+	uint8_t			*lcmd;		/* Last command sent */
+	gboolean		sup_opc;	/* The remote device supports opcodes */
+};
+
+struct mcap_mdl {
+	struct mcap_mcl		*mcl;		/* MCAP mcl for this mdl */
+	GIOChannel		*dc;		/* MCAP Data Channel IO */
+	guint			wid;		/* MDL Watcher id */
+	uint16_t		mdlid;		/* MDL id */
+	uint8_t			mdep_id;	/* MCAP Data End Point */
+	MDLState		state;		/* MDL state */
+};
+
+struct connect_mcl {
+	struct mcap_mcl		*mcl;		/* MCL for outgoing connection */
+	mcap_mcl_connect_cb	connect_cb;	/* Connect callback */
+	gpointer		user_data;	/* Callback user data */
+};
+
+typedef union {
+	mcap_mdl_operation_cb 		op;
+	mcap_mdl_operation_conf_cb	op_conf;
+	mcap_mdl_del_cb			del;
+} mcap_cb_type;
+
+struct mcap_mdl_op_cb {
+	struct mcap_mdl		*mdl;		/* MCL for outgoing connection */
+	mcap_cb_type		cb;		/* Operation callback */
+	gpointer		user_data;	/* Callback user data */
+};
+
+static void mcap_notify_error(struct mcap_mcl *mcl, GError *err);
+static int mcap_send_data(int sock, const uint8_t *buf, uint32_t size);
+static int send4B_cmd(struct mcap_mcl *mcl, uint8_t oc,
+					uint8_t rc, uint16_t mdl);
+static int send5B_cmd(struct mcap_mcl *mcl, uint8_t oc, uint8_t rc,
+					uint16_t mdl, uint8_t param);
+
+/* MCAP finite state machine functions */
+static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, int len);
+static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, int len);
+static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, int len);
+
+static void (*proc_req[])(struct mcap_mcl *mcl, uint8_t *cmd, int len) = {
+	proc_req_connected,
+	proc_req_pending,
+	proc_req_active
+	};
+
+static void default_mdl_connected_cb(struct mcap_mdl *mdl, gpointer data)
+{
+	debug("MCAP Unmanaged mdl connection");
+}
+static void default_mdl_closed_cb(struct mcap_mdl *mdl, gpointer data)
+{
+	debug("MCAP Unmanaged mdl clsoed");
+}
+static void default_mdl_deleted_cb(struct mcap_mdl *mdl, gpointer data)
+{
+	debug("MCAP Unmanaged mdl deleted");
+}
+static uint8_t default_mdl_conn_req_cb(struct mcap_mcl *mcl,
+						uint8_t mdepid, uint16_t mdlid,
+						uint8_t *conf, gpointer data)
+{
+	debug("MCAP mdl remote connection aborted");
+	/* Due to this callback is not managed this request won't be supported */
+	return MCAP_REQUEST_NOT_SUPPORTED;
+}
+static uint8_t default_mdl_reconn_req_cb(struct mcap_mdl *mdl,
+						gpointer data)
+{
+	debug("MCAP mdl remote reconnection aborted");
+	/* Due to this callback is not managed this request won't be supported */
+	return MCAP_REQUEST_NOT_SUPPORTED;
+}
+
+GQuark mcap_error_quark(void)
+{
+	return g_quark_from_static_string("mcap-error-quark");
+}
+
+static char *error2str(uint8_t rc)
+{
+	switch (rc) {
+	case MCAP_SUCCESS:
+		return "Success";
+	case MCAP_INVALID_OP_CODE:
+		return "Invalid Op Code";
+	case MCAP_INVALID_PARAM_VALUE:
+		return "Ivalid Parameter Value";
+	case MCAP_INVALID_MDEP:
+		return "Invalid MDEP";
+	case MCAP_MDEP_BUSY:
+		return "MDEP Busy";
+	case MCAP_INVALID_MDL:
+		return "Invalid MDL";
+	case MCAP_MDL_BUSY:
+		return "MDL Busy";
+	case MCAP_INVALID_OPERATION:
+		return "Invalid Operation";
+	case MCAP_RESOURCE_UNAVAILABLE:
+		return "Resource Unavailable";
+	case MCAP_UNESPECIFIED_ERROR:
+		return "Unspecified Error";
+	case MCAP_REQUEST_NOT_SUPPORTED:
+		return "Request Not Supported";
+	case MCAP_CONFIGURATION_REJECTED:
+		return "Configuration Rejected";
+	default:
+		return "Unknown Op Code";
+	}
+}
+
+static struct mcap_mcl *find_mcl(GSList *list, const bdaddr_t *addr)
+{
+	GSList *l;
+	struct mcap_mcl *mcl;
+
+	for (l = list; l; l = l->next) {
+		mcl = l->data;
+
+		if (!bacmp(&mcl->addr, addr))
+			return mcl;
+	}
+
+	return NULL;
+}
+
+static void update_mcl_state(struct mcap_mcl *mcl)
+{
+	GSList *l;
+	struct mcap_mdl *mdl;
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+
+		if (mdl->state == MDL_CONNECTED) {
+			mcl->state = MCL_ACTIVE;
+			return;
+		}
+	}
+
+	mcl->state = MCL_CONNECTED;
+}
+
+static int mcap_send_data(int sock, const uint8_t *buf, uint32_t size)
+{
+	uint32_t sent = 0;
+
+	while (sent < size) {
+		int n = send(sock, buf + sent, size - sent, 0);
+		if (n < 0)
+			return -1;
+		sent += n;
+	}
+	return 0;
+}
+
+static void mcap_send_std_opcode(struct mcap_mcl *mcl, const uint8_t *buf,
+						uint32_t size, GError **err)
+{
+	if (mcl->req != MCL_AVAILABLE) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+					"Pending request");
+		return;
+	}
+
+	if (!mcl->sup_opc) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+				    "Remote does not support standard opcodes");
+		return;
+	}
+
+	if (mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), buf, size) < 0)
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+				    "Data can't be sent, write error");
+}
+
+static int send4B_cmd(struct mcap_mcl *mcl, uint8_t oc, uint8_t rc,
+								uint16_t mdl)
+{
+	uint8_t *rsp;
+	mcap4B_rsp *rsp_err;
+	int sent;
+
+
+	rsp = g_malloc0(sizeof(mcap4B_rsp));
+
+	rsp_err = (mcap4B_rsp *)rsp;
+	rsp_err->op = oc;
+	rsp_err->rc = rc;
+	rsp_err->mdl = htons (mdl);
+
+	sent = mcap_send_data(g_io_channel_unix_get_fd(mcl->cc),
+					rsp,
+					sizeof(mcap4B_rsp));
+	g_free(rsp);
+	return sent;
+}
+
+static int send5B_cmd(struct mcap_mcl *mcl, uint8_t oc, uint8_t rc,
+						uint16_t mdl, uint8_t param)
+{
+	uint8_t *rsp;
+	mcap5B_rsp *suc;
+	int sent;
+
+	rsp = g_malloc0(sizeof(mcap5B_rsp));
+
+	suc = (mcap5B_rsp *)rsp;
+	suc->op = oc;
+	suc->rc = rc;
+	suc->mdl = htons(mdl);
+	suc->param = param;
+
+	sent = mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), rsp,
+							sizeof(mcap5B_rsp));
+	g_free(rsp);
+	return sent;
+}
+
+static uint16_t generate_mdlid(struct mcap_mcl *mcl)
+{
+	uint16_t mdlid = MCAP_MDLID_INITIAL;
+	struct mcap_mdl *mdl;
+	GSList *l;
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if (mdlid < mdl->mdlid)
+			break;
+		else
+			mdlid = mdl->mdlid + 1;
+	}
+
+	if (mdlid > MCAP_MDLID_FINAL)
+		return 0;
+
+	return mdlid;
+}
+
+static uint8_t *create_req(uint8_t op, uint16_t mdl_id)
+{
+	uint8_t *req;
+	mcap_md_req *req_cmd;
+
+	req = g_malloc0(sizeof(mcap_md_req));
+
+	req_cmd = (mcap_md_req *)req;
+	req_cmd->op = op;
+	req_cmd->mdl = htons(mdl_id);
+
+	return req;
+}
+
+static uint8_t *create_mdl_req(uint16_t mdl_id, uint8_t mdep, uint8_t conf)
+{
+	uint8_t *req;
+	mcap_md_create_mdl_req *req_mdl;
+
+	req = g_malloc0(sizeof(mcap_md_create_mdl_req));
+
+	req_mdl = (mcap_md_create_mdl_req *)req;
+	req_mdl->op = MCAP_MD_CREATE_MDL_REQ;
+	req_mdl->mdl = htons(mdl_id);
+	req_mdl->mdep = mdep;
+	req_mdl->conf = conf;
+
+	return req;
+}
+
+static gint compare_mdl(gconstpointer a, gconstpointer b)
+{
+	const struct mcap_mdl *mdla = a;
+	const struct mcap_mdl *mdlb = b;
+
+	if (mdla->mdlid == mdlb->mdlid)
+		return 0;
+	else if (mdla->mdlid < mdlb->mdlid)
+		return -1;
+	else
+		return 1;
+}
+
+static gboolean wait_response_timer(gpointer data)
+{
+	struct mcap_mcl *mcl = data;
+	struct mcap_mdl_op_cb *con = mcl->priv_data;
+	struct mcap_mdl *mdl = con->mdl;
+
+	GError *gerr = NULL;
+
+	RELEASE_TIMER(mcl);
+	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+	g_free(mdl);
+
+	mcl->req = MCL_AVAILABLE;
+	update_mcl_state(mcl);
+
+	g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
+					"Timeout waiting response");
+
+	mcap_notify_error(mcl, gerr);
+
+	g_error_free(gerr);
+	mcl->ms->mcl_closed_cb(mcl, mcl->ms->user_data);
+	mcap_close_mcl(mcl);
+	return FALSE;
+}
+
+void mcap_req_mdl_creation(struct mcap_mcl *mcl,
+				uint8_t mdepid,
+				uint8_t conf,
+				GError **err,
+				mcap_mdl_operation_conf_cb connect_cb,
+				gpointer user_data)
+{
+	struct mcap_mdl *mdl;
+	struct mcap_mdl_op_cb *con;
+	uint8_t *cmd = NULL;
+	uint16_t id;
+
+	id = generate_mdlid(mcl);
+	if (!id) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+					"Not more mdlids available");
+		return;
+	}
+
+	mdl = g_new0(struct mcap_mdl, 1);
+	mdl->mcl = mcl;
+	mdl->mdlid = id;
+	mdl->mdep_id = mdepid;
+	mdl->state = MDL_WAITING;
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = mdl;
+	con->cb.op_conf = connect_cb;
+	con->user_data = user_data;
+
+	cmd = create_mdl_req(id, mdepid, conf);
+	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_create_mdl_req), err);
+	if (*err) {
+		g_free(mdl);
+		g_free(con);
+		g_free(cmd);
+		return;
+	}
+
+	mcl->state = MCL_ACTIVE;
+	mcl->req = MCL_WAITING_RSP;
+	mcl->priv_data = con;
+	mcl->lcmd = cmd;
+	mcl->mdls = g_slist_insert_sorted(mcl->mdls, mdl, compare_mdl);
+	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl);
+}
+
+void mcap_req_mdl_reconnect(struct mcap_mdl *mdl,
+				GError **err,
+				mcap_mdl_operation_cb reconnect_cb,
+				gpointer user_data)
+{
+	struct mcap_mdl_op_cb *con;
+	struct mcap_mcl *mcl = mdl->mcl;
+	uint8_t *cmd;
+
+	if (mdl->state != MDL_CLOSED) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+					"MDL is not closed");
+		return;
+	}
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+
+	cmd = create_req(MCAP_MD_RECONNECT_MDL_REQ, mdl->mdlid);
+	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err);
+	if (*err) {
+		g_free(con);
+		g_free(cmd);
+		return;
+	}
+
+	mdl->state = MDL_WAITING;
+
+	con->mdl = mdl;
+	con->cb.op = reconnect_cb;
+	con->user_data = user_data;
+
+	mcl->lcmd = cmd;
+	mcl->req = MCL_WAITING_RSP;
+	mcl->priv_data = con;
+
+	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl);
+}
+
+void mcap_req_mdl_deletion(struct mcap_mdl *mdl, GError **err,
+			mcap_mdl_del_cb delete_cb, gpointer user_data)
+{
+	struct mcap_mcl *mcl= mdl->mcl;
+	struct mcap_mdl_op_cb *con;
+	GSList *l;
+	uint8_t *cmd;
+
+	l = g_slist_find(mcl->mdls, mdl);
+
+	if (!l) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_NOT_FOUND,
+							"Mdl not found");
+		return;
+	}
+
+	if (mdl->state == MDL_WAITING) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+					"Not valid petition in this mdl state");
+		return;
+	}
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = mdl;
+	con->cb.del = delete_cb;
+	con->user_data = user_data;
+
+	cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdl->mdlid);
+	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err);
+	if (*err) {
+		g_free(con);
+		g_free(cmd);
+		return;
+	}
+
+	mcl->lcmd = cmd;
+	mcl->req = MCL_WAITING_RSP;
+	mcl->priv_data = con;
+
+	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl);
+}
+
+void mcap_mdl_abort(struct mcap_mdl *mdl, GError **err,
+			mcap_mdl_del_cb abort_cb, gpointer user_data)
+{
+	struct mcap_mdl_op_cb *con;
+	struct mcap_mcl *mcl = mdl->mcl;
+	uint8_t *cmd;
+
+	if (mdl->state != MDL_WAITING) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+							"Mdl in invalid state");
+		return;
+	}
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	cmd = create_req(MCAP_MD_ABORT_MDL_REQ, mdl->mdlid);
+	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err);
+	if (*err) {
+		g_free(con);
+		g_free(cmd);
+		return;
+	}
+
+	con->mdl = mdl;
+	con->cb.del = abort_cb;
+	con->user_data = user_data;
+
+	mcl->lcmd = cmd;
+	mcl->req = MCL_WAITING_RSP;
+	mcl->priv_data = con;
+
+	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl);
+}
+
+int mcap_mdl_get_fd(struct mcap_mdl *mdl)
+{
+	if (mdl->dc)
+		return g_io_channel_unix_get_fd(mdl->dc);
+	return -1;
+}
+
+static void shutdown_mdl(struct mcap_mdl *mdl)
+{
+	mdl->state = MDL_CLOSED;
+
+	g_source_remove(mdl->wid);
+
+	if (mdl->dc) {
+		g_io_channel_shutdown(mdl->dc, TRUE, NULL);
+		g_io_channel_unref(mdl->dc);
+		mdl->dc = NULL;
+	}
+}
+
+static void mcap_free_mdls(struct mcap_mcl *mcl)
+{
+	GSList *l;
+	struct mcap_mdl *mdl;
+
+	if (!mcl->mdls)
+		return;
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		shutdown_mdl(mdl);
+		g_free(mdl);
+	}
+
+	g_slist_free(mcl->mdls);
+	mcl->mdls = NULL;
+}
+
+static void mcap_mcl_free(struct mcap_mcl *mcl)
+{
+	if (mcl->tid) {
+		RELEASE_TIMER(mcl);
+	}
+
+	if (mcl->cc) {
+		g_io_channel_shutdown(mcl->cc, TRUE, NULL);
+		g_io_channel_unref(mcl->cc);
+		mcl->cc = NULL;
+	}
+	g_source_remove(mcl->wid);
+	if (mcl->lcmd) {
+		g_free(mcl->lcmd);
+		mcl->lcmd = NULL;
+	}
+	if (mcl->priv_data) {
+		g_free(mcl->priv_data);
+		mcl->priv_data = NULL;
+	}
+
+	mcap_free_mdls(mcl);
+
+	if (mcl->cb)
+		g_free(mcl->cb);
+	g_free(mcl);
+}
+
+void mcap_close_mcl(struct mcap_mcl *mcl)
+{
+	if (!mcl)
+		return;
+	mcl->ms->mcls = g_slist_remove(mcl->ms->mcls, mcl);
+	mcap_mcl_free(mcl);
+}
+
+static gboolean parse_set_opts(struct mcap_mcl_cb *mcl_cb, GError **err,
+						McapMclCb cb1, va_list args)
+{
+	McapMclCb cb = cb1;
+	struct mcap_mcl_cb *c;
+
+	c = g_new0(struct mcap_mcl_cb, 1);
+
+	while (cb != MCAP_MDL_CB_INVALID) {
+		switch (cb) {
+		case MCAP_MDL_CB_CONNECTED:
+			c->mdl_connected = va_arg(args,
+				mcap_mdl_event_cb);
+			break;
+		case MCAP_MDL_CB_CLOSED:
+			c->mdl_closed = va_arg(args,
+				mcap_mdl_event_cb);
+			break;
+		case MCAP_MDL_CB_DELETED:
+			c->mdl_deleted = va_arg(args,
+				mcap_mdl_event_cb);
+			break;
+		case MCAP_MDL_CB_REMOTE_CONN_REQ:
+			c->mdl_conn_req = va_arg(args,
+				mcap_remote_mdl_conn_req_cb);
+			break;
+		case MCAP_MDL_CB_REMOTE_RECONN_REQ:
+			c->mdl_reconn_req = va_arg(args,
+				mcap_remote_mdl_reconn_req_cb);
+			break;
+		default:
+			g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+				"Unknown option %d", cb);
+			return FALSE;
+		}
+		cb = va_arg(args, int);
+	}
+
+	/* Set new callbacks set */
+	if (c->mdl_connected)
+		mcl_cb->mdl_connected = c->mdl_connected;
+	if (c->mdl_closed)
+		mcl_cb->mdl_closed = c->mdl_closed;
+	if (c->mdl_deleted)
+		mcl_cb->mdl_deleted = c->mdl_deleted;
+	if (c->mdl_conn_req)
+		mcl_cb->mdl_conn_req = c->mdl_conn_req;
+	if (c->mdl_reconn_req)
+		mcl_cb->mdl_reconn_req = c->mdl_reconn_req;
+
+	g_free(c);
+	return TRUE;
+}
+
+void mcap_mcl_set_cb(struct mcap_mcl *mcl, GError **gerr,
+					gpointer user_data, McapMclCb cb1, ...)
+{
+	va_list args;
+	gboolean ret;
+
+	va_start(args, cb1);
+	ret = parse_set_opts(mcl->cb, gerr, cb1, args);
+	va_end(args);
+
+	if (!ret)
+		return;
+
+	mcl->cb->user_data = user_data;
+	return;
+}
+
+bdaddr_t mcap_mcl_get_addr(struct mcap_mcl *mcl)
+{
+	return mcl->addr;
+}
+
+static void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, int len)
+{
+	switch (cmd[0]) {
+	case MCAP_MD_SYNC_CAP_REQ:
+		debug("TODO: received MCAP_MD_SYNC_CAP_REQ: %d",
+			MCAP_MD_SYNC_CAP_REQ);
+		break;
+	case MCAP_MD_SYNC_CAP_RSP:
+		debug("TODO: received MCAP_MD_SYNC_CAP_RSP: %d",
+			MCAP_MD_SYNC_CAP_RSP);
+		break;
+	case MCAP_MD_SYNC_SET_REQ:
+		debug("TODO: received MCAP_MD_SYNC_SET_REQ: %d",
+			MCAP_MD_SYNC_SET_REQ);
+		break;
+	case MCAP_MD_SYNC_SET_RSP:
+		debug("TODO: received MCAP_MD_SYNC_SET_RSP: %d",
+			MCAP_MD_SYNC_SET_RSP);
+		break;
+	case MCAP_MD_SYNC_INFO_IND:
+		debug("TODO: received MCAP_MD_SYNC_INFO_IND :%d",
+			MCAP_MD_SYNC_INFO_IND);
+		break;
+        }
+}
+
+static void error_cmd_rsp(struct mcap_mcl *mcl, uint8_t *cmd, int len)
+{
+	uint16_t mdlr;
+
+	if ((cmd[0] >= MCAP_ERROR_RSP) && (cmd[0] <= MCAP_MD_DELETE_MDL_RSP)) {
+		/* Standard Op Code request is invalid in current state */
+		error("Invalid cmd received (op code = %d) in state %d",
+							cmd[0],	mcl->state);
+		/* Get mdlid sended to generate appropiate response if it is possible */
+		mdlr = len < sizeof(mcap_md_req) ? MCAP_MDLID_RESERVED :
+						ntohs(((mcap_md_req *)cmd)->mdl);
+		send4B_cmd(mcl, cmd[0]+1, MCAP_INVALID_OPERATION, mdlr);
+	} else {
+		error("Unknown cmd request received (op code = %d) in state %d",
+							cmd[0], mcl->state);
+		send4B_cmd(mcl, MCAP_ERROR_RSP, MCAP_INVALID_OP_CODE,
+							MCAP_MDLID_RESERVED);
+	}
+}
+
+static struct mcap_mdl *get_mdl(struct mcap_mcl *mcl, uint16_t mdlid)
+{
+	GSList *l;
+	struct mcap_mdl *mdl;
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if (mdlid == mdl->mdlid)
+			return mdl;
+	}
+
+	return NULL;
+}
+
+/* Functions used to process request */
+
+static void process_md_create_mdl_req(struct mcap_mcl *mcl, uint8_t *cmd, int len)
+{
+	mcap_md_create_mdl_req *req;
+	struct mcap_mdl *mdl;
+	uint16_t mdl_id;
+	uint8_t mdep_id;
+	uint8_t cfga, conf;
+	uint8_t rsp;
+
+	if (len != sizeof(mcap_md_create_mdl_req)) {
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP,
+				MCAP_INVALID_PARAM_VALUE, MCAP_MDLID_RESERVED);
+		return;
+	}
+
+	req = (mcap_md_create_mdl_req *)cmd;
+
+	mdl_id = ntohs(req->mdl);
+	if ((mdl_id < MCAP_MDLID_INITIAL) || (mdl_id > MCAP_MDLID_FINAL)) {
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDL, mdl_id);
+		return;
+	}
+
+	mdep_id = req->mdep;
+	if (mdep_id > MCAP_MDEPID_FINAL) {
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDEP, mdl_id);
+		return;
+	}
+
+	cfga = conf = req->conf;
+	/* Callback to upper layer */
+	rsp = mcl->cb->mdl_conn_req(mcl, mdep_id, mdl_id, &conf,
+							mcl->cb->user_data);
+
+	if ((cfga != 0) && (cfga != conf)) {
+		/* Remote device set default configuration but upper profile */
+		/* has changed it. Protocol Error: force closing the MCL by */
+		/* using remote device using UNESPECIFIED_ERROR response*/
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_UNESPECIFIED_ERROR,
+								mdl_id);
+		return;
+	}
+	if (rsp != MCAP_SUCCESS) {
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, rsp, mdl_id);
+		return;
+	}
+
+	mdl = get_mdl(mcl, mdl_id);
+	if (!mdl) {
+		mdl = g_malloc0(sizeof(struct mcap_mdl));
+		mdl->mcl = mcl;
+		mdl->mdlid = mdl_id;
+	} else if (mdl->state == MDL_CONNECTED) {
+		shutdown_mdl(mdl);
+		mcl->cb->mdl_closed(mdl, mcl->cb->user_data);
+	}
+	mdl->state = MDL_WAITING;
+	mdl->mdep_id = mdep_id;
+	mcl->mdls = g_slist_insert_sorted(mcl->mdls, mdl, compare_mdl);
+
+	mcl->state = MCL_PENDING;
+	send5B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_SUCCESS, mdl_id, conf);
+}
+
+static void process_md_reconnect_mdl_req(struct mcap_mcl *mcl, uint8_t *cmd,
+									int len)
+{
+	mcap_md_req *req;
+	struct mcap_mdl *mdl;
+	uint16_t mdl_id;
+	uint8_t rsp;
+	gboolean close;
+
+	if (len != sizeof(mcap_md_req)) {
+		send4B_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP,
+				MCAP_INVALID_PARAM_VALUE, MCAP_MDLID_RESERVED);
+		return;
+	}
+
+	req = (mcap_md_req *)cmd;
+	mdl_id = ntohs(req->mdl);
+
+	mdl = get_mdl(mcl, mdl_id);
+	if (!mdl) {
+		send4B_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP,
+						MCAP_INVALID_MDL, mdl_id);
+		return;
+	}
+
+	close = mdl->state == MDL_CONNECTED;
+	if (close)
+		shutdown_mdl(mdl);
+
+	/* Callback to upper layer */
+	rsp = mcl->cb->mdl_reconn_req(mdl, mcl->cb->user_data);
+	if (rsp != MCAP_SUCCESS) {
+		if (close)
+			mcl->cb->mdl_closed(mdl, mcl->cb->user_data);
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, rsp, mdl_id);
+		return;
+	}
+	mdl->state = MDL_WAITING;
+	mcl->state = MCL_PENDING;
+	send4B_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_SUCCESS, mdl_id);
+}
+
+static void process_md_abort_mdl_req(struct mcap_mcl *mcl, uint8_t *cmd, int len)
+{
+	mcap_md_req *req;
+	GSList *l;
+	struct mcap_mdl *mdl, *del;
+	uint16_t mdl_id;
+
+	if (len != sizeof(mcap_md_req)) {
+		send4B_cmd(mcl, MCAP_MD_ABORT_MDL_RSP,
+				MCAP_INVALID_PARAM_VALUE, MCAP_MDLID_RESERVED);
+		return;
+	}
+
+	req = (mcap_md_req *)cmd;
+	mdl_id = ntohs(req->mdl);
+	mcl->state = MCL_CONNECTED;
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if ((mdl_id == mdl->mdlid) && (mdl->state == MDL_WAITING)) {
+			del = mdl;
+			if (mcl->state != MCL_CONNECTED)
+				break;
+			continue;
+		}
+		if ((mdl->state == MDL_CONNECTED) && (mcl->state != MCL_ACTIVE))
+			mcl->state = MCL_ACTIVE;
+
+		if ((del) && (mcl->state == MCL_ACTIVE))
+			break;
+	}
+
+	if (!del) {
+		send4B_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_INVALID_MDL, mdl_id);
+		return;
+	}
+
+	mcl->mdls = g_slist_remove(mcl->mdls, del);
+	g_free(del);
+	send4B_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_SUCCESS, mdl_id);
+}
+/* Functions used to process responses */
+static gboolean check_err_rsp(uint16_t rmdl, uint16_t smdl, uint8_t rc,
+					int rlen, int len, GError **gerr)
+{
+	gboolean close = FALSE;
+	char *msg;
+
+	if (rmdl != smdl) {
+		msg = "MDLID received doesn't match with MDLID sended";
+		close = TRUE;
+		goto fail;
+	}
+
+	if (rc != MCAP_SUCCESS) {
+		msg = error2str(rc);
+		goto fail;
+	}
+
+	if (rlen < len) {
+		msg = "Protocol error";
+		close = TRUE;
+		goto fail;
+	}
+	return FALSE;
+fail:
+	g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_FAILED, "%s", msg);
+	return close;
+}
+
+static gboolean process_md_create_mdl_rsp(struct mcap_mcl *mcl,
+							uint8_t *cmd, int len)
+{
+	struct mcap_mdl_op_cb *conn = mcl->priv_data;
+	struct mcap_mdl *mdl = conn->mdl;
+	mcap_mdl_operation_conf_cb connect_cb = conn->cb.op_conf;
+	gpointer user_data = conn->user_data;
+	uint16_t mdlid;
+	mcap5B_rsp *rsp = (mcap5B_rsp *) cmd;
+	mcap_md_create_mdl_req *cmdlast;
+	GError *gerr = NULL;
+	gboolean close = FALSE;
+
+	g_free(mcl->priv_data);
+	mcl->priv_data = NULL;
+
+	cmdlast = (mcap_md_create_mdl_req *) mcl->lcmd;
+	mdlid = ntohs(cmdlast->mdl);
+	rsp->mdl = ntohs(rsp->mdl);
+
+	g_free(mcl->lcmd);
+	mcl->lcmd = NULL;
+	mcl->req = MCL_AVAILABLE;
+	close = check_err_rsp(rsp->mdl, mdlid, rsp->rc, 0, 0, &gerr);
+
+	if (gerr)
+		goto fail;
+
+	if (len < 5) {
+		g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
+							"Protocol error");
+		close = TRUE;
+		goto fail;
+	}
+
+	/* Check if preferences changed */
+	if ((cmdlast->conf != 0x00) && (rsp->param != cmdlast->conf)) {
+		g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
+							"Configutation changed");
+		close = TRUE;
+		goto fail;
+	}
+
+	connect_cb(mdl, rsp->param, gerr, user_data);
+	return close;
+fail:
+	connect_cb(NULL, 0, gerr, user_data);
+	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+	g_free(mdl);
+	g_error_free(gerr);
+	return close;
+}
+
+static gboolean process_md_reconnect_mdl_rsp(struct mcap_mcl *mcl,
+							uint8_t *cmd, int len)
+{
+	struct mcap_mdl_op_cb *reconn = mcl->priv_data;
+	struct mcap_mdl *mdl = reconn->mdl;
+	mcap_mdl_operation_cb reconn_cb = reconn->cb.op;
+	gpointer user_data = reconn->user_data;
+	mcap4B_rsp *rsp = (mcap4B_rsp *) cmd;
+	mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd;
+	uint16_t mdlid = ntohs(cmdlast->mdl);
+	GError *gerr = NULL;
+	gboolean close = FALSE;
+
+	g_free(mcl->priv_data);
+	mcl->priv_data = NULL;
+
+	rsp->mdl = ntohs(rsp->mdl);
+
+	close = check_err_rsp(rsp->mdl, mdlid, rsp->rc, len,
+						sizeof(mcap4B_rsp), &gerr);
+
+	g_free(mcl->lcmd);
+	mcl->lcmd = NULL;
+	mcl->req = MCL_AVAILABLE;
+
+	reconn_cb(mdl, gerr, user_data);
+	if (gerr)
+		g_error_free(gerr);
+
+	return close;
+}
+
+static gboolean process_md_abort_mdl_rsp(struct mcap_mcl *mcl,
+							uint8_t *cmd, int len)
+{
+	struct mcap_mdl_op_cb *abrt = mcl->priv_data;
+	struct mcap_mdl *mdl = abrt->mdl;
+	mcap_mdl_del_cb abrt_cb = abrt->cb.del;
+	gpointer user_data = abrt->user_data;
+	mcap4B_rsp *rsp = (mcap4B_rsp *) cmd;
+	mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd;
+	uint16_t mdlid = ntohs(cmdlast->mdl);
+	GError *gerr = NULL;
+	gboolean close = FALSE;
+
+	g_free(mcl->priv_data);
+	mcl->priv_data = NULL;
+
+	rsp->mdl = ntohs(rsp->mdl);
+
+	close = check_err_rsp(rsp->mdl, mdlid, rsp->rc, len,
+						sizeof(mcap4B_rsp), &gerr);
+	g_free(mcl->lcmd);
+	mcl->lcmd = NULL;
+	mcl->req = MCL_AVAILABLE;
+
+	if (gerr) {
+		abrt_cb(gerr, user_data);
+		g_error_free(gerr);
+		return close;
+	}
+
+	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+	g_free(mdl);
+	update_mcl_state(mcl);
+	abrt_cb(gerr, user_data);
+	return close;
+}
+
+static void mcap_delete_mdl(gpointer elem, gpointer user_data)
+{
+	struct mcap_mdl *mdl = elem;
+	gboolean notify = *(gboolean *)user_data;
+	if (mdl->state == MDL_CONNECTED) {
+		debug("MDL %d already connected, closing it", mdl->mdlid);
+		shutdown_mdl(mdl);
+	}
+	if (notify)
+		mdl->mcl->cb->mdl_deleted(mdl, mdl->mcl->cb->user_data);
+	g_free(mdl);
+}
+
+static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, uint8_t *cmd,
+									int len)
+{
+	struct mcap_mdl_op_cb *del = mcl->priv_data;
+	struct mcap_mdl *mdl = del->mdl;
+	mcap_mdl_del_cb deleted_cb = del->cb.del;
+	gpointer user_data = del->user_data;
+	mcap4B_rsp *rsp = (mcap4B_rsp *) cmd;
+	mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd;
+	uint16_t mdlid = ntohs(cmdlast->mdl);
+	GError *gerr = NULL;
+	gboolean close = FALSE;
+	gboolean notify = FALSE;
+
+	g_free(mcl->priv_data);
+	mcl->priv_data = NULL;
+
+	rsp->mdl = ntohs(rsp->mdl);
+
+	close = check_err_rsp(rsp->mdl, mdlid, rsp->rc, len,
+						sizeof(mcap4B_rsp), &gerr);
+
+	g_free(mcl->lcmd);
+	mcl->lcmd = NULL;
+	mcl->req = MCL_AVAILABLE;
+	if (gerr) {
+		deleted_cb(gerr, user_data);
+		g_error_free(gerr);
+		return close;
+	}
+
+	if (mdlid == MCAP_ALL_MDLIDS) {
+		g_slist_foreach(mcl->mdls, mcap_delete_mdl, &notify);
+		g_slist_free(mcl->mdls);
+		mcl->mdls = NULL;
+		mcl->state = MCL_CONNECTED;
+		goto end;
+	}
+
+	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+	update_mcl_state(mcl);
+	mcap_delete_mdl(mdl, &notify);
+end:
+	deleted_cb(gerr, user_data);
+	return close;
+}
+
+static void process_md_delete_mdl_req(struct mcap_mcl *mcl, mcap_md_req *req)
+{
+	struct mcap_mdl *mdl, *aux;
+	uint16_t mdlid = ntohs(req->mdl);
+	gboolean notify;
+	GSList *l;
+
+	debug("process MCAP_MD_DELETE_MDL_REQ");
+
+	if (mdlid == MCAP_ALL_MDLIDS) {
+		notify = FALSE;
+		g_slist_foreach(mcl->mdls, mcap_delete_mdl, &notify);
+		g_slist_free(mcl->mdls);
+		mcl->state = MCL_CONNECTED;
+		mcl->mdls = NULL;
+		/* NULL mdl means ALL_MDLS */
+		mcl->cb->mdl_deleted(NULL, mcl->cb->user_data);
+		goto resp;
+	}
+
+	if ((mdlid < MCAP_MDLID_INITIAL) || (mdlid > MCAP_MDLID_FINAL)) {
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDL, mdlid);
+		return;
+	}
+
+	for (l=mcl->mdls, mdl = NULL; l; l = l->next) {
+		aux = l->data;
+		if (aux->mdlid == mdlid) {
+			mdl = aux;
+			break;
+		}
+	}
+
+	if (!mdl) {
+		send4B_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL, mdlid);
+		return;
+	}
+
+	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+	update_mcl_state(mcl);
+	notify = TRUE;
+	mcap_delete_mdl(mdl, &notify);
+resp:
+	send4B_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_SUCCESS, mdlid);
+}
+
+/* Function used to process commands depending of MCL state */
+
+static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, int len)
+{
+	debug("Processing in Connected");
+	switch (cmd[0]) {
+	case MCAP_MD_CREATE_MDL_REQ:
+		process_md_create_mdl_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_RECONNECT_MDL_REQ:
+		process_md_reconnect_mdl_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_DELETE_MDL_REQ:
+		process_md_delete_mdl_req(mcl, (mcap_md_req *) cmd);
+		break;
+	default:
+		error_cmd_rsp(mcl, cmd, len);
+	}
+}
+
+static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, int len)
+{
+	debug("Processing in Pending");
+	if (cmd[0] == MCAP_MD_ABORT_MDL_REQ)
+		process_md_abort_mdl_req(mcl, cmd, len);
+	else
+		error_cmd_rsp(mcl, cmd, len);
+}
+
+static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, int len)
+{
+	debug("Processing in Active");
+	switch (cmd[0]) {
+	case MCAP_MD_CREATE_MDL_REQ:
+		process_md_create_mdl_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_RECONNECT_MDL_REQ:
+		process_md_reconnect_mdl_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_DELETE_MDL_REQ:
+		process_md_delete_mdl_req(mcl, (mcap_md_req *) cmd);
+		break;
+	default:
+		error_cmd_rsp(mcl, cmd, len);
+	}
+}
+
+static void mcap_notify_error(struct mcap_mcl *mcl, GError *err)
+{
+	struct mcap_mdl_op_cb *con = mcl->priv_data;
+
+	if (!con || !mcl->lcmd)
+		return;
+
+	switch (mcl->lcmd[0]){
+	case MCAP_MD_CREATE_MDL_REQ:
+		con->cb.op_conf(NULL, 0, err, con->user_data);
+		break;
+	case MCAP_MD_ABORT_MDL_REQ:
+	case MCAP_MD_DELETE_MDL_REQ:
+		con->cb.del(err, con->user_data);
+		break;
+	case MCAP_MD_RECONNECT_MDL_REQ:
+		con->cb.op(NULL, err, con->user_data);
+		break;
+	}
+
+	g_free(mcl->priv_data);
+	mcl->priv_data = NULL;
+}
+
+static gboolean check_rsp(struct mcap_mcl *mcl, uint8_t *cmd, int len)
+{
+	mcap4B_rsp *rsp;
+	GError *err = NULL;
+
+	/* Check if the response matches with the last request */
+	if ((cmd[0] != MCAP_ERROR_RSP) && ((mcl->lcmd[0] + 1) != cmd[0]))
+		goto close_mcl;
+
+	if (len < 4)
+		goto close_mcl;
+	rsp = (mcap4B_rsp *)cmd;
+
+	if (rsp->rc == MCAP_REQUEST_NOT_SUPPORTED) {
+		debug("Remote does not support opcodes");
+		g_set_error(&err, MCAP_ERROR, MCAP_ERROR_FAILED,
+				"Remote does not support this operation");
+		mcap_notify_error(mcl, err);
+		g_error_free(err);
+
+		g_free(mcl->lcmd);
+		mcl->lcmd = NULL;
+		mcl->sup_opc = FALSE;
+		mcl->req = MCL_AVAILABLE;
+		update_mcl_state(mcl);
+		return FALSE;
+	}
+
+	if (rsp->rc == MCAP_UNESPECIFIED_ERROR)
+		goto close_mcl;
+
+	return TRUE;
+close_mcl:
+	g_set_error(&err, MCAP_ERROR, MCAP_ERROR_FAILED,
+					"Protocol error");
+	mcap_notify_error(mcl, err);
+	g_error_free(err);
+	mcl->ms->mcl_closed_cb(mcl, mcl->ms->user_data);
+	mcap_close_mcl(mcl);
+	return FALSE;
+}
+
+static void proc_response(struct mcap_mcl *mcl, uint8_t *cmd, int len)
+{
+	gboolean close;
+	RELEASE_TIMER(mcl);
+
+	if (!check_rsp(mcl, cmd, len))
+		return;
+
+	switch (cmd[0]) {
+	case MCAP_ERROR_RSP:
+		debug("received MCAP_ERROR_RSP:%d",MCAP_ERROR_RSP);
+		close = TRUE;
+		break;
+	case MCAP_MD_CREATE_MDL_RSP:
+		close = process_md_create_mdl_rsp(mcl, cmd, len);
+		break;
+	case MCAP_MD_RECONNECT_MDL_RSP:
+		close = process_md_reconnect_mdl_rsp(mcl, cmd, len);
+		break;
+	case MCAP_MD_ABORT_MDL_RSP:
+		close = process_md_abort_mdl_rsp(mcl, cmd, len);
+		break;
+	case MCAP_MD_DELETE_MDL_RSP:
+		debug("received MCAP_MD_DELETE_MDL_RSP");
+		close = process_md_delete_mdl_rsp(mcl, cmd, len);
+		break;
+	default:
+		debug("Unknown cmd response received (op code = %d)",cmd[0]);
+		close = TRUE;
+		break;
+	}
+
+	if (close) {
+		mcl->ms->mcl_closed_cb(mcl, mcl->ms->user_data);
+		mcap_close_mcl(mcl);
+	}
+}
+
+static void rsend_req(struct mcap_mcl *mcl)
+{
+	uint8_t *cmd = mcl->lcmd;
+	int len;
+
+	if(!cmd)
+		return;
+
+	switch (cmd[0]) {
+	case MCAP_MD_RECONNECT_MDL_REQ:
+	case MCAP_MD_ABORT_MDL_REQ:
+	case MCAP_MD_DELETE_MDL_REQ:
+		len = 3;
+		break;
+	case MCAP_MD_CREATE_MDL_REQ:
+		len = 5;
+		break;
+	default:
+		return;
+	}
+
+	mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), cmd, len);
+}
+
+static void proc_cmd(struct mcap_mcl *mcl, uint8_t *cmd, int len)
+{
+	if ((cmd[0] >= MCAP_MD_SYNC_CAP_REQ) && (cmd[0] <= MCAP_MD_SYNC_INFO_IND)) {
+		proc_sync_cmd(mcl, cmd, len);
+		return;
+	}
+
+	if (!mcl->sup_opc) {
+		/* In case the remote device doesn't work corerctly */
+		error("Remote device does not support opcodes, cmd ignored");
+		return;
+	}
+
+	if (mcl->req == MCL_WAITING_RSP) {
+		if (cmd[0] & 0x01) {
+			/* Request arrived when a response is expected */
+			if (mcl->role == MCL_INITIATOR)
+				/* ignore */
+				return;
+			proc_req[mcl->state](mcl, cmd, len);
+			/* Initiator will ignore our last request => re-send */
+			rsend_req(mcl);
+			return;
+		}
+		proc_response(mcl, cmd, len);
+	} else if (cmd[0] & 0x01) {
+		proc_req[mcl->state](mcl, cmd, len);
+	}
+}
+
+static gboolean mdl_closing_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+
+	struct mcap_mdl *mdl = data;
+	gboolean open;
+
+	debug("Close MDL %d", mdl->mdlid);
+
+	open = (mdl->state == MDL_CONNECTED);
+	shutdown_mdl(mdl);
+
+	update_mcl_state(mdl->mcl);
+
+	if (open)
+		/*Callback to upper layer */
+		mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
+
+	return FALSE;
+}
+
+static void mcap_connect_mdl_cb(GIOChannel *chan, GError *conn_err,
+							gpointer data)
+{
+	struct mcap_mdl_op_cb *con = data;
+	struct mcap_mdl *mdl = con->mdl;
+	mcap_mdl_operation_cb cb = con->cb.op;
+	gpointer user_data = con->user_data;
+
+	g_free(con);
+	debug("mdl connect callback");
+
+	if (conn_err) {
+		debug("ERROR: mdl connect callback");
+		mdl->state = MDL_CLOSED;
+		g_io_channel_unref(mdl->dc);
+		mdl->dc = NULL;
+		cb(mdl, conn_err, user_data);
+		return;
+	}
+
+	mdl->state = MDL_CONNECTED;
+	mdl->wid = g_io_add_watch(mdl->dc, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+				(GIOFunc) mdl_closing_cb, mdl);
+
+	cb(mdl, conn_err, user_data);
+}
+
+void mcap_mdl_connect(struct mcap_mdl *mdl, BtIOType BtType, uint16_t dcpsm,
+	GError **err, mcap_mdl_operation_cb connect_cb, gpointer user_data)
+{
+	struct mcap_mdl_op_cb *con;
+
+	if (mdl->state != MDL_WAITING) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+					"MDL has not been negotiated.");
+		return;
+	}
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = mdl;
+	con->cb.op = connect_cb;
+	con->user_data = user_data;
+
+	/* TODO: Check if BtIOType is ERTM or Streaming before continue */
+
+	mdl->dc = bt_io_connect(BtType, mcap_connect_mdl_cb, con,
+				NULL, err,
+				BT_IO_OPT_SOURCE_BDADDR, &mdl->mcl->ms->src,
+				BT_IO_OPT_DEST_BDADDR, &mdl->mcl->addr,
+				BT_IO_OPT_PSM, dcpsm,
+				BT_IO_OPT_MTU, MCAP_DC_MTU,
+				BT_IO_OPT_SEC_LEVEL, mdl->mcl->ms->sec,
+				BT_IO_OPT_INVALID);
+	if (*err) {
+		debug("MDL Connection error");
+		mdl->state = MDL_CLOSED;
+		g_free(con);
+	}
+}
+
+static gboolean mcl_control_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+
+	struct mcap_mcl *mcl = data;
+	int sk, len;
+	uint8_t buf[MCAP_CC_MTU];
+
+	if (cond & G_IO_NVAL) {
+		error("Revise G_IO_NVAL");
+		goto fail;
+	}
+
+	if (cond & (G_IO_ERR | G_IO_HUP))
+		goto fail;
+
+	sk = g_io_channel_unix_get_fd(chan);
+	len = recv(sk, buf, sizeof(buf), 0);
+	if (len < 0)
+		goto fail;
+
+	proc_cmd(mcl, buf, len);
+	return TRUE;
+fail:
+	mcl->ms->mcl_closed_cb(mcl, mcl->ms->user_data);
+	mcap_close_mcl(mcl);
+	return FALSE;
+}
+
+static void mcap_connect_mcl_cb(GIOChannel *chan, GError *conn_err,
+							gpointer user_data)
+{
+	char dstaddr[18];
+	struct connect_mcl *con = user_data;
+	struct mcap_mcl *aux, *mcl = con->mcl;
+	mcap_mcl_connect_cb connect_cb = con->connect_cb;
+	gpointer data = con->user_data;
+	GError *gerr = NULL;
+
+	g_free(con);
+
+	if (conn_err) {
+		mcap_close_mcl(mcl);
+		connect_cb(NULL, conn_err, data);
+		return;
+	}
+
+	ba2str(&mcl->addr, dstaddr);
+
+	aux = find_mcl(mcl->ms->mcls, &mcl->addr);
+	if (aux) {
+		mcap_close_mcl(mcl);
+		error("MCL error: Device %s is already connected", dstaddr);
+		g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS,
+				"MCL %s is already connected", dstaddr);
+		connect_cb(NULL, gerr, data);
+		g_error_free(gerr);
+		return;
+	}
+
+	mcl->state = MCL_CONNECTED;
+	mcl->role = MCL_INITIATOR;
+	mcl->req = MCL_AVAILABLE;
+	mcl->sup_opc = TRUE;
+
+	mcl->ms->mcls = g_slist_prepend(mcl->ms->mcls, mcl);
+	mcl->wid = g_io_add_watch(mcl->cc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+				(GIOFunc) mcl_control_cb, mcl);
+	connect_cb(mcl, gerr, data);
+}
+
+void mcap_create_mcl(struct mcap_session *ms,
+				const bdaddr_t *addr,
+				uint16_t ccpsm,
+				GError **err,
+				mcap_mcl_connect_cb connect_cb,
+				gpointer user_data)
+{
+	struct mcap_mcl *mcl;
+	struct connect_mcl *con;
+
+	mcl = find_mcl(ms->mcls, addr);
+	if (mcl) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS,
+					"MCL is already connected.");
+		return;
+	}
+
+	mcl = g_new0(struct mcap_mcl, 1);
+	mcl->ms = ms;
+	bacpy(&mcl->addr, addr);
+
+	con = g_new0(struct connect_mcl, 1);
+	mcl->cb = g_new0(struct mcap_mcl_cb, 1);
+	SET_DEFAULT_MCL_CB(mcl);
+
+	con->mcl = mcl;
+	con->connect_cb = connect_cb;
+	con->user_data = user_data;
+
+	mcl->cc = bt_io_connect(BT_IO_L2CAP, mcap_connect_mcl_cb, con,
+				NULL, err,
+				BT_IO_OPT_SOURCE_BDADDR, &ms->src,
+				BT_IO_OPT_DEST_BDADDR, addr,
+				BT_IO_OPT_PSM, ccpsm,
+				BT_IO_OPT_MTU, MCAP_CC_MTU,
+				BT_IO_OPT_SEC_LEVEL, ms->sec,
+				BT_IO_OPT_INVALID);
+	if (*err) {
+		g_free(con);
+		mcap_close_mcl(mcl);
+	}
+}
+
+static void connect_dc_event_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct mcap_mdl *mdl = user_data;
+	struct mcap_mcl *mcl = mdl->mcl;
+
+	mdl->state = MDL_CONNECTED;
+	mdl->dc = g_io_channel_ref(chan);
+	mdl->wid = g_io_add_watch(mdl->dc, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+						(GIOFunc) mdl_closing_cb, mdl);
+
+	mcl->state = MCL_ACTIVE;
+	mcl->cb->mdl_connected(mdl, mcl->cb->user_data);
+}
+
+static void confirm_dc_event_cb(GIOChannel *chan, gpointer user_data)
+{
+	struct mcap_session *ms = user_data;
+	struct mcap_mcl *mcl;
+	struct mcap_mdl *mdl;
+	GError *err = NULL;
+	bdaddr_t dst;
+	GSList *l;
+
+	bt_io_get(chan, BT_IO_L2CAP, &err,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	mcl = find_mcl(ms->mcls, &dst);
+	if (!mcl || (mcl->state != MCL_PENDING))
+		goto drop;
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if (mdl->state == MDL_WAITING) {
+			if (!bt_io_accept(chan, connect_dc_event_cb, mdl, NULL,
+									&err)) {
+				error("MDL accept error %s", err->message);
+				g_error_free(err);
+				goto drop;
+			}
+			return;
+		}
+	}
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void connect_mcl_event_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct mcap_mcl *mcl = user_data;
+
+	if (err) {
+		mcap_mcl_free(mcl);
+		return;
+	}
+	mcl->cb = g_new0(struct mcap_mcl_cb, 1);
+	mcl->state = MCL_CONNECTED;
+	mcl->role = MCL_ACCEPTOR;
+	mcl->req = MCL_AVAILABLE;
+	mcl->cc = g_io_channel_ref(chan);
+	mcl->sup_opc = TRUE;
+
+	SET_DEFAULT_MCL_CB(mcl);
+	debug("MCL created");
+	mcl->ms->mcls = g_slist_prepend(mcl->ms->mcls, mcl);
+
+	mcl->wid = g_io_add_watch(mcl->cc,
+			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+			(GIOFunc) mcl_control_cb, mcl);
+
+	/* Callback to report new entry MCL */
+	mcl->ms->mcl_created_cb(mcl, mcl->ms->user_data);
+}
+
+static void confirm_mcl_event_cb(GIOChannel *chan, gpointer user_data)
+{
+	struct mcap_session *ms = user_data;
+	struct mcap_mcl *mcl;
+	bdaddr_t dst;
+	char address[18], srcstr[18];
+	GError *err = NULL;
+
+	bt_io_get(chan, BT_IO_L2CAP, &err,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	ba2str(&ms->src, srcstr);
+	mcl = find_mcl(ms->mcls, &dst);
+	if (mcl) {
+		error("Control channel already created with %s on adapter %s",
+				address, srcstr);
+		goto drop;
+	}
+
+	mcl = g_new0(struct mcap_mcl, 1);
+
+	mcl->ms = ms;
+	bacpy(&mcl->addr, &dst);
+
+	if (!bt_io_accept(chan, connect_mcl_event_cb, mcl, NULL, &err)) {
+		error("mcap accpet error: %s", err->message);
+		mcap_mcl_free(mcl);
+		g_error_free(err);
+		goto drop;
+	}
+
+	return;
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+struct mcap_session *mcap_create_session(struct btd_adapter *btd_adapter,
+					BtIOSecLevel sec,
+					uint16_t ccpsm,
+					uint16_t dcpsm,
+					GError **gerr,
+					mcap_mcl_event_cb mcl_closed,
+					mcap_mcl_event_cb mcl_created,
+					gpointer user_data)
+{
+	struct mcap_session *ms;
+
+	if (sec < BT_IO_SEC_MEDIUM) {
+		g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+				"Security level can't be minor of %d",
+				BT_IO_SEC_MEDIUM);
+		return NULL;
+	}
+
+	if (!(mcl_closed && mcl_created)) {
+		g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+				"The callbacks can't be null");
+		return NULL;
+	}
+
+	ms = g_new0(struct mcap_session, 1);
+
+	adapter_get_address(btd_adapter, &ms->src);
+
+	ms->sec = sec;
+	ms->mcl_closed_cb = mcl_closed;
+	ms->mcl_created_cb = mcl_created;
+	ms->user_data = user_data;
+
+	/* Listen incoming connections in control channel */
+	ms->ccio = bt_io_listen(BT_IO_L2CAP, NULL, confirm_mcl_event_cb, ms,
+				NULL, gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &ms->src,
+				BT_IO_OPT_PSM, ccpsm,
+				BT_IO_OPT_MTU, MCAP_CC_MTU,
+				BT_IO_OPT_SEC_LEVEL, sec,
+				BT_IO_OPT_INVALID);
+	if (!ms->ccio) {
+		error("%s", (*gerr)->message);
+		g_free(ms);
+		return NULL;
+	}
+
+	/* Listen incoming connections in data channels */
+	ms->dcio = bt_io_listen(BT_IO_L2CAP, NULL, confirm_dc_event_cb, ms,
+				NULL, gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &ms->src,
+				BT_IO_OPT_PSM, dcpsm,
+				BT_IO_OPT_MTU, MCAP_DC_MTU,
+				BT_IO_OPT_SEC_LEVEL, sec,
+				BT_IO_OPT_INVALID);
+	if (!ms->dcio) {
+		g_io_channel_shutdown(ms->ccio, TRUE, NULL);
+		g_io_channel_unref(ms->ccio);
+		ms->ccio = NULL;
+		error("%s", (*gerr)->message);
+		g_free(ms);
+		return NULL;
+	}
+
+	return ms;
+}
+
+void mcap_close_session(struct mcap_session *ms)
+{
+	GSList *l;
+
+	if (!ms)
+		return;
+
+	if (ms->ccio) {
+		g_io_channel_shutdown(ms->ccio, TRUE, NULL);
+		g_io_channel_unref(ms->ccio);
+		ms->ccio = NULL;
+	}
+
+	if (ms->dcio) {
+		g_io_channel_shutdown(ms->dcio, TRUE, NULL);
+		g_io_channel_unref(ms->dcio);
+		ms->dcio = NULL;
+	}
+
+	for (l = ms->mcls; l; l = l->next)
+		mcap_mcl_free(l->data);
+
+	g_slist_free(ms->mcls);
+	ms->mcls = NULL;
+
+	g_free(ms);
+}
diff --git a/mcap/mcap.h b/mcap/mcap.h
new file mode 100644
index 0000000..7b45d04
--- /dev/null
+++ b/mcap/mcap.h
@@ -0,0 +1,174 @@
+/*
+ *
+ *  MCAP for BlueZ- Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010 Santiago Carot-Nemesio <sancane at gmail.com>
+ *  Copyright (C) 2009-2010 Jose Antonio Santos Cadenas <santoscadenas at gmail.com>
+ *  Copyright (C) 2009-2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __MCAP_H
+#define __MCAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MCAP_VERSION	0x0100	/* current version 01.00 */
+
+/* bytes to get MCAP Supported Procedures */
+#define MCAP_SUP_PROC	0x06
+
+/* maximum transmission unit for channels */
+#define MCAP_CC_MTU	48
+#define MCAP_DC_MTU	L2CAP_DEFAULT_MTU
+
+
+/* MCAP Standard Op Codes */
+#define MCAP_ERROR_RSP			0x00
+#define MCAP_MD_CREATE_MDL_REQ		0x01
+#define MCAP_MD_CREATE_MDL_RSP		0x02
+#define MCAP_MD_RECONNECT_MDL_REQ	0x03
+#define MCAP_MD_RECONNECT_MDL_RSP	0x04
+#define MCAP_MD_ABORT_MDL_REQ		0x05
+#define MCAP_MD_ABORT_MDL_RSP		0x06
+#define MCAP_MD_DELETE_MDL_REQ		0x07
+#define MCAP_MD_DELETE_MDL_RSP		0x08
+/*RESERVED                              0x09*/
+/*RESERVED                              0x10*/
+
+/* MCAP Clock Sync Op Codes */
+#define MCAP_MD_SYNC_CAP_REQ		0x11
+#define MCAP_MD_SYNC_CAP_RSP		0x12
+#define MCAP_MD_SYNC_SET_REQ		0x13
+#define MCAP_MD_SYNC_SET_RSP            0x14
+#define MCAP_MD_SYNC_INFO_IND		0x15
+/*RESERVED				0x16*/
+/*RESERVED                              0x17*/
+/*RESERVED                              0x18*/
+/*RESERVED                              0x19*/
+/*RESERVED                              0x20*/
+
+/* MCAP Response codes */
+#define MCAP_SUCCESS			0x00
+#define MCAP_INVALID_OP_CODE		0x01
+#define MCAP_INVALID_PARAM_VALUE	0x02
+#define MCAP_INVALID_MDEP		0x03
+#define MCAP_MDEP_BUSY			0x04
+#define MCAP_INVALID_MDL		0x05
+#define MCAP_MDL_BUSY			0x06
+#define MCAP_INVALID_OPERATION		0x07
+#define MCAP_RESOURCE_UNAVAILABLE	0x08
+#define MCAP_UNESPECIFIED_ERROR		0x09
+#define MCAP_REQUEST_NOT_SUPPORTED	0x0A
+#define MCAP_CONFIGURATION_REJECTED	0x0B
+/*RESERVED				0x0C-0xFF*/
+
+
+/* MDL IDs */
+#define MCAP_MDLID_RESERVED		0x0000
+#define MCAP_MDLID_INITIAL		0x0001
+#define MCAP_MDLID_FINAL		0xFEFF
+/*RESERVED				0xFF00-0xFFFE*/
+#define MCAP_ALL_MDLIDS			0xFFFF
+
+/* MDEP IDs */
+#define MCAP_MDEPID_INITIAL		0x00
+#define MCAP_MDEPID_FINAL		0x7F
+/*RESERVED				0x80-0xFF*/
+
+
+/*
+ * MCAP Request Packet Format
+ */
+
+typedef struct {
+	uint8_t         op;
+        uint16_t	mdl;
+	uint8_t		mdep;
+	uint8_t		conf;
+} __attribute__ ((packed)) mcap_md_create_mdl_req;
+
+typedef struct {
+        uint8_t         op;
+        uint16_t        mdl;
+} __attribute__ ((packed)) mcap_md_req;
+
+
+/*
+ * MCAP Response Packet Format
+ */
+
+typedef struct {
+        uint8_t         op;
+        uint8_t         rc;
+	uint16_t        mdl;
+} __attribute__ ((packed)) mcap4B_rsp;
+
+typedef struct {
+        uint8_t         op;
+        uint8_t         rc;
+        uint16_t        mdl;
+        uint8_t         param;
+} __attribute__ ((packed)) mcap5B_rsp;
+
+
+/*
+ * MCAP Clock Synchronization Protocol
+ */
+typedef struct {
+        uint8_t         op;
+        uint16_t        timest;
+} __attribute__ ((packed)) mcap_md_sync_cap_req;
+
+typedef struct {
+        uint8_t         op;
+	uint8_t         rc;
+	uint8_t         btclock;
+        uint16_t        sltime;
+	uint16_t        timestnr;
+	uint16_t        timestna;
+} __attribute__ ((packed)) mcap_md_sync_cap_rsp;
+
+typedef struct {
+        uint8_t         op;
+	uint8_t         timestui;
+        uint32_t        btclock;
+	uint64_t        timestst;
+} __attribute__ ((packed)) mcap_md_sync_set_req;
+
+typedef struct {
+        uint8_t         op;
+	uint8_t         rc;
+        uint32_t        btclock;
+	uint64_t        timestst;
+	uint16_t        timestsa;
+} __attribute__ ((packed)) mcap_md_sync_set_rsp;
+
+typedef struct {
+        uint8_t         op;
+        uint32_t        btclock;
+	uint64_t        timestst;
+	uint16_t        timestsa;
+} __attribute__ ((packed)) mcap_md_sync_info_ind;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MCAP_H */
diff --git a/mcap/mcap_lib.h b/mcap/mcap_lib.h
new file mode 100644
index 0000000..530f03a
--- /dev/null
+++ b/mcap/mcap_lib.h
@@ -0,0 +1,137 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 Santiago Carot Nemesio <sancane at gmail.com>
+ *  Copyright (C) 2010 Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __MCAP_LIB_H
+#define __MCAP_LIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <pthread.h>
+#include <bluetooth/l2cap.h>
+#include "btio.h"
+#include "mcap.h"
+
+typedef enum {
+	MCAP_ERROR_INVALID_ARGS,
+	MCAP_ERROR_MEMORY,
+	MCAP_ERROR_ALREADY_EXISTS,
+	MCAP_ERROR_FAILED,
+	MCAP_ERROR_NOT_FOUND,
+} McapError;
+
+typedef enum {
+	MCAP_MDL_CB_INVALID,
+	MCAP_MDL_CB_CONNECTED,
+	MCAP_MDL_CB_CLOSED,
+	MCAP_MDL_CB_DELETED,
+	MCAP_MDL_CB_REMOTE_CONN_REQ,
+	MCAP_MDL_CB_REMOTE_RECONN_REQ,
+} McapMclCb;
+
+struct mcap_session;
+struct mcap_mcl;
+struct mcap_mdl;
+
+/************ Callbacks ************/
+
+/* mdl callbacks */
+
+typedef void (* mcap_mdl_event_cb) (struct mcap_mdl *mdl, gpointer data);
+typedef void (* mcap_mdl_operation_conf_cb) (struct mcap_mdl *mdl, uint8_t conf,
+						GError *err, gpointer data);
+typedef void (* mcap_mdl_operation_cb) (struct mcap_mdl *mdl, GError *err,
+						gpointer data);
+typedef void (* mcap_mdl_del_cb) (GError *err, gpointer data);
+
+/* Next function should return an MCAP appropiate response code */
+typedef uint8_t (* mcap_remote_mdl_conn_req_cb) (struct mcap_mcl *mcl,
+						uint8_t mdepid, uint16_t mdlid,
+						uint8_t *conf, gpointer data);
+typedef uint8_t (* mcap_remote_mdl_reconn_req_cb) (struct mcap_mdl *mdl,
+						gpointer data);
+
+/* mcl callbacks */
+
+typedef void (* mcap_mcl_event_cb) (struct mcap_mcl *mcl, gpointer data);
+typedef void (* mcap_mcl_connect_cb) (struct mcap_mcl *mcl, GError *err,
+								gpointer data);
+
+/************ Operations ************/
+
+/* Mdl operations*/
+
+void mcap_req_mdl_creation(struct mcap_mcl *mcl,
+				uint8_t mdepid,
+				uint8_t conf,
+				GError **err,
+				mcap_mdl_operation_conf_cb connect_cb,
+				gpointer user_data);
+void mcap_req_mdl_reconnect(struct mcap_mdl *mdl, GError **err,
+				mcap_mdl_operation_cb reconnect_cb,
+				gpointer user_data);
+void mcap_req_mdl_deletion(struct mcap_mdl *mdl, GError **err,
+			mcap_mdl_del_cb delete_cb, gpointer user_data);
+void mcap_mdl_connect(struct mcap_mdl *mdl,
+			BtIOType BtType,
+			uint16_t dcpsm,
+			GError **err,
+			mcap_mdl_operation_cb connect_cb,
+			gpointer user_data);
+void mcap_mdl_abort(struct mcap_mdl *mdl, GError **err,
+			mcap_mdl_del_cb abort_cb, gpointer user_data);
+int mcap_mdl_get_fd(struct mcap_mdl *mdl);
+
+/* Mcl operations*/
+
+void mcap_create_mcl(struct mcap_session *ms,
+				const bdaddr_t *addr,
+				uint16_t ccpsm,
+				GError **err,
+				mcap_mcl_connect_cb connect_cb,
+				gpointer user_data);
+void mcap_close_mcl(struct mcap_mcl *mcl);
+void mcap_mcl_set_cb(struct mcap_mcl *mcl, GError **gerr,
+					gpointer user_data, McapMclCb cb1, ...);
+bdaddr_t mcap_mcl_get_addr(struct mcap_mcl *mcl);
+
+/* MCAP main operations */
+
+struct mcap_session *mcap_create_session(struct btd_adapter *btd_adapter,
+					BtIOSecLevel sec, uint16_t ccpsm,
+					uint16_t dcpsm,
+					GError **gerr,
+					mcap_mcl_event_cb mcl_closed,
+					mcap_mcl_event_cb mcl_created,
+					gpointer user_data);
+
+void mcap_close_session(struct mcap_session *ms);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MCAP_LIB_H */
+
-- 
1.6.3.3




^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH] Added support for deleting all MDLS in MCAP
  2010-04-06 14:00 [PATCH] Multi-Channel Adaptation Protocol Santiago Carot Nemesio
@ 2010-04-09 11:44 ` José Antonio Santos Cadenas
  2010-04-20 16:44   ` Elvis Pfützenreuter
  2010-05-04  8:20   ` José Antonio Santos Cadenas
  0 siblings, 2 replies; 4+ messages in thread
From: José Antonio Santos Cadenas @ 2010-04-09 11:44 UTC (permalink / raw)
  To: Santiago Carot Nemesio; +Cc: linux-bluetooth


Also fixed some bugs in mcl state transitions

Signed-off-by: Jose Antonio Santos Cadenas <santoscadenas@gmail.com>
Reviewed-by: Santiago Carot Nemesio <sancane@gmail.com>
---
 mcap/mcap.c     |  133 +++++++++++++++++++++++++++++++++++++++----------------
 mcap/mcap_lib.h |    2 +
 2 files changed, 97 insertions(+), 38 deletions(-)

diff --git a/mcap/mcap.c b/mcap/mcap.c
index 28c586c..e76c565 100644
--- a/mcap/mcap.c
+++ b/mcap/mcap.c
@@ -33,7 +33,6 @@
 #include "mcap.h"
 #include "mcap_lib.h"

-//#define STATE2STR(_mcl) state2str(_mcl->state)
 #define MCAP_ERROR mcap_error_quark()
 #define SET_DEFAULT_MCL_CB(__mcl) do {				\
 	__mcl->cb->mdl_connected = default_mdl_connected_cb;	\
@@ -71,7 +70,8 @@ typedef enum {
 typedef enum {
 	MDL_WAITING,
 	MDL_CONNECTED,
-	MDL_CLOSED
+	MDL_DELETING,
+	MDL_CLOSED,
 } MDLState;

 struct mcap_mcl_cb {
@@ -79,7 +79,7 @@ struct mcap_mcl_cb {
 	mcap_mdl_event_cb 		mdl_closed;	/* Remote device has closed an mdl */
 	mcap_mdl_event_cb 		mdl_deleted;	/* Remote device deleted an mdl */
 	mcap_remote_mdl_conn_req_cb	mdl_conn_req;	/* Remote deive requested create an mdl */
-	mcap_remote_mdl_reconn_req_cb 	mdl_reconn_req;	/* Remote device requested reconnect previus mdl */
+	mcap_remote_mdl_reconn_req_cb 	mdl_reconn_req;	/* Remote device requested reconnect previous mdl */
 	gpointer			user_data;	/* user data */
 };

@@ -240,6 +240,9 @@ static void update_mcl_state(struct mcap_mcl *mcl)
 	GSList *l;
 	struct mcap_mdl *mdl;

+	if (mcl->state == MCL_PENDING)
+		return;
+
 	for (l = mcl->mdls; l; l = l->next) {
 		mdl = l->data;

@@ -280,6 +283,12 @@ static void mcap_send_std_opcode(struct mcap_mcl *mcl, const uint8_t *buf,
 		return;
 	}

+	if (mcl->state == MCL_PENDING) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+			"Not Std Op. Codes can be sent in PENDING State");
+		return;
+	}
+
 	if (mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), buf, size) < 0)
 		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
 				    "Data can't be sent, write error");
@@ -394,17 +403,10 @@ static gint compare_mdl(gconstpointer a, gconstpointer b)
 static gboolean wait_response_timer(gpointer data)
 {
 	struct mcap_mcl *mcl = data;
-	struct mcap_mdl_op_cb *con = mcl->priv_data;
-	struct mcap_mdl *mdl = con->mdl;

 	GError *gerr = NULL;

 	RELEASE_TIMER(mcl);
-	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
-	g_free(mdl);
-
-	mcl->req = MCL_AVAILABLE;
-	update_mcl_state(mcl);

 	g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
 					"Timeout waiting response");
@@ -494,6 +496,26 @@ void mcap_req_mdl_reconnect(struct mcap_mdl *mdl,
 	con->cb.op = reconnect_cb;
 	con->user_data = user_data;

+	mcl->state = MCL_ACTIVE;
+	mcl->lcmd = cmd;
+	mcl->req = MCL_WAITING_RSP;
+	mcl->priv_data = con;
+
+	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl);
+}
+
+static void send_delete_req(GError **err, struct mcap_mcl *mcl,
+				struct mcap_mdl_op_cb *con, uint16_t mdlid)
+{
+	uint8_t *cmd;
+
+	cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdlid);
+	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err);
+	if (*err) {
+		g_free(cmd);
+		return;
+	}
+
 	mcl->lcmd = cmd;
 	mcl->req = MCL_WAITING_RSP;
 	mcl->priv_data = con;
@@ -501,13 +523,43 @@ void mcap_req_mdl_reconnect(struct mcap_mdl *mdl,
 	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl);
 }

+void mcap_req_mdl_delete_all(struct mcap_mcl *mcl, GError **err,
+			mcap_mdl_del_cb delete_cb, gpointer user_data)
+{
+	GSList *l;
+	struct mcap_mdl *mdl;
+	struct mcap_mdl_op_cb *con;
+
+	debug("MCL in state: %d", mcl->state);
+	if (!mcl->mdls) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+				"There are not MDLs created");
+		return;
+	}
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if (mdl->state != MDL_WAITING)
+			mdl->state = MDL_DELETING;
+	}
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = NULL;
+	con->cb.del = delete_cb;
+	con->user_data = user_data;
+
+	send_delete_req(err, mcl, con, MCAP_ALL_MDLIDS);
+	if (*err)
+		g_free(con);
+	debug("exiting MCL in state: %d", mcl->state);
+}
+
 void mcap_req_mdl_deletion(struct mcap_mdl *mdl, GError **err,
 			mcap_mdl_del_cb delete_cb, gpointer user_data)
 {
 	struct mcap_mcl *mcl= mdl->mcl;
 	struct mcap_mdl_op_cb *con;
 	GSList *l;
-	uint8_t *cmd;

 	l = g_slist_find(mcl->mdls, mdl);

@@ -519,28 +571,19 @@ void mcap_req_mdl_deletion(struct mcap_mdl *mdl, GError **err,

 	if (mdl->state == MDL_WAITING) {
 		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
-					"Not valid petition in this mdl state");
+							"Mdl is not created");
 		return;
 	}
+	mdl->state = MDL_DELETING;

 	con = g_new0(struct mcap_mdl_op_cb, 1);
 	con->mdl = mdl;
 	con->cb.del = delete_cb;
 	con->user_data = user_data;

-	cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdl->mdlid);
-	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err);
-	if (*err) {
+	send_delete_req(err, mcl, con, mdl->mdlid);
+	if (*err)
 		g_free(con);
-		g_free(cmd);
-		return;
-	}
-
-	mcl->lcmd = cmd;
-	mcl->req = MCL_WAITING_RSP;
-	mcl->priv_data = con;
-
-	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl);
 }

 void mcap_mdl_abort(struct mcap_mdl *mdl, GError **err,
@@ -688,7 +731,7 @@ static gboolean parse_set_opts(struct mcap_mcl_cb *mcl_cb, GError **err,
 		cb = va_arg(args, int);
 	}

-	/* Set new callbacks set */
+	/* Set new callbacks */
 	if (c->mdl_connected)
 		mcl_cb->mdl_connected = c->mdl_connected;
 	if (c->mdl_closed)
@@ -825,7 +868,7 @@ static void process_md_create_mdl_req(struct mcap_mcl *mcl, uint8_t *cmd, int le
 	if ((cfga != 0) && (cfga != conf)) {
 		/* Remote device set default configuration but upper profile */
 		/* has changed it. Protocol Error: force closing the MCL by */
-		/* using remote device using UNESPECIFIED_ERROR response*/
+		/* using remote device using UNESPECIFIED_ERROR response */
 		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_UNESPECIFIED_ERROR,
 								mdl_id);
 		return;
@@ -844,8 +887,8 @@ static void process_md_create_mdl_req(struct mcap_mcl *mcl, uint8_t *cmd, int le
 		shutdown_mdl(mdl);
 		mcl->cb->mdl_closed(mdl, mcl->cb->user_data);
 	}
-	mdl->state = MDL_WAITING;
 	mdl->mdep_id = mdep_id;
+	mdl->state = MDL_WAITING;
 	mcl->mdls = g_slist_insert_sorted(mcl->mdls, mdl, compare_mdl);

 	mcl->state = MCL_PENDING;
@@ -934,7 +977,7 @@ static void process_md_abort_mdl_req(struct mcap_mcl *mcl, uint8_t *cmd, int len
 	g_free(del);
 	send4B_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_SUCCESS, mdl_id);
 }
-/* Functions used to process responses */
+
 static gboolean check_err_rsp(uint16_t rmdl, uint16_t smdl, uint8_t rc,
 					int rlen, int len, GError **gerr)
 {
@@ -1089,15 +1132,26 @@ static void mcap_delete_mdl(gpointer elem, gpointer user_data)
 {
 	struct mcap_mdl *mdl = elem;
 	gboolean notify = *(gboolean *)user_data;
-	if (mdl->state == MDL_CONNECTED) {
-		debug("MDL %d already connected, closing it", mdl->mdlid);
-		shutdown_mdl(mdl);
-	}
+
+	shutdown_mdl(mdl);
 	if (notify)
 		mdl->mcl->cb->mdl_deleted(mdl, mdl->mcl->cb->user_data);
 	g_free(mdl);
 }

+static void restore_mdl(gpointer elem, gpointer data)
+{
+	struct mcap_mdl *mdl = elem;
+
+	if (mdl->state == MDL_DELETING) {
+		if (mdl->dc)
+			mdl->state = MDL_CONNECTED;
+		else
+			mdl->state = MDL_CLOSED;
+	} else if (mdl->state == MDL_CLOSED)
+		mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
+}
+
 static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, uint8_t *cmd,
 									int len)
 {
@@ -1124,6 +1178,10 @@ static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, uint8_t *cmd,
 	mcl->lcmd = NULL;
 	mcl->req = MCL_AVAILABLE;
 	if (gerr) {
+		if (mdl)
+			restore_mdl(mdl, NULL);
+		else
+			g_slist_foreach(mcl->mdls, restore_mdl, NULL);
 		deleted_cb(gerr, user_data);
 		g_error_free(gerr);
 		return close;
@@ -1158,8 +1216,8 @@ static void process_md_delete_mdl_req(struct mcap_mcl *mcl, mcap_md_req *req)
 		notify = FALSE;
 		g_slist_foreach(mcl->mdls, mcap_delete_mdl, &notify);
 		g_slist_free(mcl->mdls);
-		mcl->state = MCL_CONNECTED;
 		mcl->mdls = NULL;
+		mcl->state = MCL_CONNECTED;
 		/* NULL mdl means ALL_MDLS */
 		mcl->cb->mdl_deleted(NULL, mcl->cb->user_data);
 		goto resp;
@@ -1178,11 +1236,10 @@ static void process_md_delete_mdl_req(struct mcap_mcl *mcl, mcap_md_req *req)
 		}
 	}

-	if (!mdl) {
+	if (!mdl || (mdl->state == MDL_WAITING)) {
 		send4B_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL, mdlid);
 		return;
 	}
-
 	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
 	update_mcl_state(mcl);
 	notify = TRUE;
@@ -1400,16 +1457,16 @@ static gboolean mdl_closing_cb(GIOChannel *chan, GIOCondition cond, gpointer dat
 {

 	struct mcap_mdl *mdl = data;
-	gboolean open;
+	gboolean notify;

 	debug("Close MDL %d", mdl->mdlid);

-	open = (mdl->state == MDL_CONNECTED);
+	notify = (mdl->state == MDL_CONNECTED);
 	shutdown_mdl(mdl);

 	update_mcl_state(mdl->mcl);

-	if (open)
+	if (notify)
 		/*Callback to upper layer */
 		mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);

diff --git a/mcap/mcap_lib.h b/mcap/mcap_lib.h
index 530f03a..867a53d 100644
--- a/mcap/mcap_lib.h
+++ b/mcap/mcap_lib.h
@@ -92,6 +92,8 @@ void mcap_req_mdl_creation(struct mcap_mcl *mcl,
 void mcap_req_mdl_reconnect(struct mcap_mdl *mdl, GError **err,
 				mcap_mdl_operation_cb reconnect_cb,
 				gpointer user_data);
+void mcap_req_mdl_delete_all(struct mcap_mcl *mcl, GError **err,
+			mcap_mdl_del_cb delete_cb, gpointer user_data);
 void mcap_req_mdl_deletion(struct mcap_mdl *mdl, GError **err,
 			mcap_mdl_del_cb delete_cb, gpointer user_data);
 void mcap_mdl_connect(struct mcap_mdl *mdl,
--
1.6.3.3


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] Added support for deleting all MDLS in MCAP
  2010-04-09 11:44 ` [PATCH] Added support for deleting all MDLS in MCAP José Antonio Santos Cadenas
@ 2010-04-20 16:44   ` Elvis Pfützenreuter
  2010-05-04  8:20   ` José Antonio Santos Cadenas
  1 sibling, 0 replies; 4+ messages in thread
From: Elvis Pfützenreuter @ 2010-04-20 16:44 UTC (permalink / raw)
  To: jcaden; +Cc: Santiago Carot Nemesio, linux-bluetooth

Taking a look on these.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] Added support for deleting all MDLS in MCAP
  2010-04-09 11:44 ` [PATCH] Added support for deleting all MDLS in MCAP José Antonio Santos Cadenas
  2010-04-20 16:44   ` Elvis Pfützenreuter
@ 2010-05-04  8:20   ` José Antonio Santos Cadenas
  1 sibling, 0 replies; 4+ messages in thread
From: José Antonio Santos Cadenas @ 2010-05-04  8:20 UTC (permalink / raw)
  To: Santiago Carot Nemesio; +Cc: linux-bluetooth

Hi all,

El Friday 09 April 2010 13:44:41 José Antonio Santos Cadenas escribió:
> Also fixed some bugs in mcl state transitions
> 
> Signed-off-by: Jose Antonio Santos Cadenas <santoscadenas@gmail.com>
> Reviewed-by: Santiago Carot Nemesio <sancane@gmail.com>
> ---
>  mcap/mcap.c     |  133
> +++++++++++++++++++++++++++++++++++++++---------------- mcap/mcap_lib.h | 
>   2 +
>  2 files changed, 97 insertions(+), 38 deletions(-)
> 
> diff --git a/mcap/mcap.c b/mcap/mcap.c
> index 28c586c..e76c565 100644
> --- a/mcap/mcap.c
> +++ b/mcap/mcap.c
> @@ -33,7 +33,6 @@
>  #include "mcap.h"
>  #include "mcap_lib.h"
> 
> -//#define STATE2STR(_mcl) state2str(_mcl->state)
>  #define MCAP_ERROR mcap_error_quark()
>  #define SET_DEFAULT_MCL_CB(__mcl) do {				\
>  	__mcl->cb->mdl_connected = default_mdl_connected_cb;	\
> @@ -71,7 +70,8 @@ typedef enum {
>  typedef enum {
>  	MDL_WAITING,
>  	MDL_CONNECTED,
> -	MDL_CLOSED
> +	MDL_DELETING,
> +	MDL_CLOSED,
>  } MDLState;
> 
>  struct mcap_mcl_cb {
> @@ -79,7 +79,7 @@ struct mcap_mcl_cb {
>  	mcap_mdl_event_cb 		mdl_closed;	/* Remote device has closed an 
mdl */
>  	mcap_mdl_event_cb 		mdl_deleted;	/* Remote device deleted an 
mdl */
>  	mcap_remote_mdl_conn_req_cb	mdl_conn_req;	/* Remote deive requested
> create an mdl */ -	mcap_remote_mdl_reconn_req_cb 	mdl_reconn_req;	/*
> Remote device requested reconnect previus mdl */
> +	mcap_remote_mdl_reconn_req_cb 	mdl_reconn_req;	/* Remote device
> requested reconnect previous mdl */ gpointer			user_data;	/* 
user data */
>  };
> 
> @@ -240,6 +240,9 @@ static void update_mcl_state(struct mcap_mcl *mcl)
>  	GSList *l;
>  	struct mcap_mdl *mdl;
> 
> +	if (mcl->state == MCL_PENDING)
> +		return;
> +
>  	for (l = mcl->mdls; l; l = l->next) {
>  		mdl = l->data;
> 
> @@ -280,6 +283,12 @@ static void mcap_send_std_opcode(struct mcap_mcl *mcl,
> const uint8_t *buf, return;
>  	}
> 
> +	if (mcl->state == MCL_PENDING) {
> +		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
> +			"Not Std Op. Codes can be sent in PENDING State");
> +		return;
> +	}
> +
>  	if (mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), buf, size) < 0)
>  		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
>  				    "Data can't be sent, write error");
> @@ -394,17 +403,10 @@ static gint compare_mdl(gconstpointer a,
> gconstpointer b) static gboolean wait_response_timer(gpointer data)
>  {
>  	struct mcap_mcl *mcl = data;
> -	struct mcap_mdl_op_cb *con = mcl->priv_data;
> -	struct mcap_mdl *mdl = con->mdl;
> 
>  	GError *gerr = NULL;
> 
>  	RELEASE_TIMER(mcl);
> -	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
> -	g_free(mdl);
> -
> -	mcl->req = MCL_AVAILABLE;
> -	update_mcl_state(mcl);
> 
>  	g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
>  					"Timeout waiting response");
> @@ -494,6 +496,26 @@ void mcap_req_mdl_reconnect(struct mcap_mdl *mdl,
>  	con->cb.op = reconnect_cb;
>  	con->user_data = user_data;
> 
> +	mcl->state = MCL_ACTIVE;
> +	mcl->lcmd = cmd;
> +	mcl->req = MCL_WAITING_RSP;
> +	mcl->priv_data = con;
> +
> +	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
> mcl); +}
> +
> +static void send_delete_req(GError **err, struct mcap_mcl *mcl,
> +				struct mcap_mdl_op_cb *con, uint16_t mdlid)
> +{
> +	uint8_t *cmd;
> +
> +	cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdlid);
> +	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err);
> +	if (*err) {
> +		g_free(cmd);
> +		return;
> +	}
> +
>  	mcl->lcmd = cmd;
>  	mcl->req = MCL_WAITING_RSP;
>  	mcl->priv_data = con;
> @@ -501,13 +523,43 @@ void mcap_req_mdl_reconnect(struct mcap_mdl *mdl,
>  	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
> mcl); }
> 
> +void mcap_req_mdl_delete_all(struct mcap_mcl *mcl, GError **err,
> +			mcap_mdl_del_cb delete_cb, gpointer user_data)
> +{
> +	GSList *l;
> +	struct mcap_mdl *mdl;
> +	struct mcap_mdl_op_cb *con;
> +
> +	debug("MCL in state: %d", mcl->state);
> +	if (!mcl->mdls) {
> +		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
> +				"There are not MDLs created");
> +		return;
> +	}
> +
> +	for (l = mcl->mdls; l; l = l->next) {
> +		mdl = l->data;
> +		if (mdl->state != MDL_WAITING)
> +			mdl->state = MDL_DELETING;
> +	}
> +
> +	con = g_new0(struct mcap_mdl_op_cb, 1);
> +	con->mdl = NULL;
> +	con->cb.del = delete_cb;
> +	con->user_data = user_data;
> +
> +	send_delete_req(err, mcl, con, MCAP_ALL_MDLIDS);
> +	if (*err)
> +		g_free(con);
> +	debug("exiting MCL in state: %d", mcl->state);
> +}
> +
>  void mcap_req_mdl_deletion(struct mcap_mdl *mdl, GError **err,
>  			mcap_mdl_del_cb delete_cb, gpointer user_data)
>  {
>  	struct mcap_mcl *mcl= mdl->mcl;
>  	struct mcap_mdl_op_cb *con;
>  	GSList *l;
> -	uint8_t *cmd;
> 
>  	l = g_slist_find(mcl->mdls, mdl);
> 
> @@ -519,28 +571,19 @@ void mcap_req_mdl_deletion(struct mcap_mdl *mdl,
> GError **err,
> 
>  	if (mdl->state == MDL_WAITING) {
>  		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
> -					"Not valid petition in this mdl state");
> +							"Mdl is not created");
>  		return;
>  	}
> +	mdl->state = MDL_DELETING;
> 
>  	con = g_new0(struct mcap_mdl_op_cb, 1);
>  	con->mdl = mdl;
>  	con->cb.del = delete_cb;
>  	con->user_data = user_data;
> 
> -	cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdl->mdlid);
> -	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err);
> -	if (*err) {
> +	send_delete_req(err, mcl, con, mdl->mdlid);
> +	if (*err)
>  		g_free(con);
> -		g_free(cmd);
> -		return;
> -	}
> -
> -	mcl->lcmd = cmd;
> -	mcl->req = MCL_WAITING_RSP;
> -	mcl->priv_data = con;
> -
> -	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
> mcl); }
> 
>  void mcap_mdl_abort(struct mcap_mdl *mdl, GError **err,
> @@ -688,7 +731,7 @@ static gboolean parse_set_opts(struct mcap_mcl_cb
> *mcl_cb, GError **err, cb = va_arg(args, int);
>  	}
> 
> -	/* Set new callbacks set */
> +	/* Set new callbacks */
>  	if (c->mdl_connected)
>  		mcl_cb->mdl_connected = c->mdl_connected;
>  	if (c->mdl_closed)
> @@ -825,7 +868,7 @@ static void process_md_create_mdl_req(struct mcap_mcl
> *mcl, uint8_t *cmd, int le if ((cfga != 0) && (cfga != conf)) {
>  		/* Remote device set default configuration but upper profile */
>  		/* has changed it. Protocol Error: force closing the MCL by */
> -		/* using remote device using UNESPECIFIED_ERROR response*/
> +		/* using remote device using UNESPECIFIED_ERROR response */
>  		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_UNESPECIFIED_ERROR,
>  								mdl_id);
>  		return;
> @@ -844,8 +887,8 @@ static void process_md_create_mdl_req(struct mcap_mcl
> *mcl, uint8_t *cmd, int le shutdown_mdl(mdl);
>  		mcl->cb->mdl_closed(mdl, mcl->cb->user_data);
>  	}
> -	mdl->state = MDL_WAITING;
>  	mdl->mdep_id = mdep_id;
> +	mdl->state = MDL_WAITING;
>  	mcl->mdls = g_slist_insert_sorted(mcl->mdls, mdl, compare_mdl);
> 
>  	mcl->state = MCL_PENDING;
> @@ -934,7 +977,7 @@ static void process_md_abort_mdl_req(struct mcap_mcl
> *mcl, uint8_t *cmd, int len g_free(del);
>  	send4B_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_SUCCESS, mdl_id);
>  }
> -/* Functions used to process responses */
> +
>  static gboolean check_err_rsp(uint16_t rmdl, uint16_t smdl, uint8_t rc,
>  					int rlen, int len, GError **gerr)
>  {
> @@ -1089,15 +1132,26 @@ static void mcap_delete_mdl(gpointer elem, gpointer
> user_data) {
>  	struct mcap_mdl *mdl = elem;
>  	gboolean notify = *(gboolean *)user_data;
> -	if (mdl->state == MDL_CONNECTED) {
> -		debug("MDL %d already connected, closing it", mdl->mdlid);
> -		shutdown_mdl(mdl);
> -	}
> +
> +	shutdown_mdl(mdl);
>  	if (notify)
>  		mdl->mcl->cb->mdl_deleted(mdl, mdl->mcl->cb->user_data);
>  	g_free(mdl);
>  }
> 
> +static void restore_mdl(gpointer elem, gpointer data)
> +{
> +	struct mcap_mdl *mdl = elem;
> +
> +	if (mdl->state == MDL_DELETING) {
> +		if (mdl->dc)
> +			mdl->state = MDL_CONNECTED;
> +		else
> +			mdl->state = MDL_CLOSED;
> +	} else if (mdl->state == MDL_CLOSED)
> +		mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
> +}
> +
>  static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, uint8_t
> *cmd, int len)
>  {
> @@ -1124,6 +1178,10 @@ static gboolean process_md_delete_mdl_rsp(struct
> mcap_mcl *mcl, uint8_t *cmd, mcl->lcmd = NULL;
>  	mcl->req = MCL_AVAILABLE;
>  	if (gerr) {
> +		if (mdl)
> +			restore_mdl(mdl, NULL);
> +		else
> +			g_slist_foreach(mcl->mdls, restore_mdl, NULL);
>  		deleted_cb(gerr, user_data);
>  		g_error_free(gerr);
>  		return close;
> @@ -1158,8 +1216,8 @@ static void process_md_delete_mdl_req(struct mcap_mcl
> *mcl, mcap_md_req *req) notify = FALSE;
>  		g_slist_foreach(mcl->mdls, mcap_delete_mdl, &notify);
>  		g_slist_free(mcl->mdls);
> -		mcl->state = MCL_CONNECTED;
>  		mcl->mdls = NULL;
> +		mcl->state = MCL_CONNECTED;
>  		/* NULL mdl means ALL_MDLS */
>  		mcl->cb->mdl_deleted(NULL, mcl->cb->user_data);
>  		goto resp;
> @@ -1178,11 +1236,10 @@ static void process_md_delete_mdl_req(struct
> mcap_mcl *mcl, mcap_md_req *req) }
>  	}
> 
> -	if (!mdl) {
> +	if (!mdl || (mdl->state == MDL_WAITING)) {
>  		send4B_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL, mdlid);
>  		return;
>  	}
> -
>  	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
>  	update_mcl_state(mcl);
>  	notify = TRUE;
> @@ -1400,16 +1457,16 @@ static gboolean mdl_closing_cb(GIOChannel *chan,
> GIOCondition cond, gpointer dat {
> 
>  	struct mcap_mdl *mdl = data;
> -	gboolean open;
> +	gboolean notify;
> 
>  	debug("Close MDL %d", mdl->mdlid);
> 
> -	open = (mdl->state == MDL_CONNECTED);
> +	notify = (mdl->state == MDL_CONNECTED);
>  	shutdown_mdl(mdl);
> 
>  	update_mcl_state(mdl->mcl);
> 
> -	if (open)
> +	if (notify)
>  		/*Callback to upper layer */
>  		mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
> 
> diff --git a/mcap/mcap_lib.h b/mcap/mcap_lib.h
> index 530f03a..867a53d 100644
> --- a/mcap/mcap_lib.h
> +++ b/mcap/mcap_lib.h
> @@ -92,6 +92,8 @@ void mcap_req_mdl_creation(struct mcap_mcl *mcl,
>  void mcap_req_mdl_reconnect(struct mcap_mdl *mdl, GError **err,
>  				mcap_mdl_operation_cb reconnect_cb,
>  				gpointer user_data);
> +void mcap_req_mdl_delete_all(struct mcap_mcl *mcl, GError **err,
> +			mcap_mdl_del_cb delete_cb, gpointer user_data);
>  void mcap_req_mdl_deletion(struct mcap_mdl *mdl, GError **err,
>  			mcap_mdl_del_cb delete_cb, gpointer user_data);
>  void mcap_mdl_connect(struct mcap_mdl *mdl,


Please, dismiss this patches we are sending new ones shortly.

Regards

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2010-05-04  8:20 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-04-06 14:00 [PATCH] Multi-Channel Adaptation Protocol Santiago Carot Nemesio
2010-04-09 11:44 ` [PATCH] Added support for deleting all MDLS in MCAP José Antonio Santos Cadenas
2010-04-20 16:44   ` Elvis Pfützenreuter
2010-05-04  8:20   ` José Antonio Santos Cadenas

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.