Now profiles/health/ , android/health and mcaptest will use profiles/health/mcap-lib. --- Makefile.plugins | 4 +- android/Android.mk | 4 +- android/Makefile.am | 4 +- android/health.c | 2 +- android/mcaptest.c | 2 +- profiles/health/hdp.c | 3 +- profiles/health/hdp_util.c | 3 +- {android => profiles/health}/mcap-lib.c | 0 {android => profiles/health}/mcap-lib.h | 0 profiles/health/mcap.c | 2192 ------------------------------- profiles/health/mcap.h | 164 --- profiles/health/mcap_internal.h | 137 -- profiles/health/mcap_lib.h | 224 ---- profiles/health/mcap_sync.c | 1009 -------------- 14 files changed, 9 insertions(+), 3739 deletions(-) rename {android => profiles/health}/mcap-lib.c (100%) rename {android => profiles/health}/mcap-lib.h (100%) delete mode 100644 profiles/health/mcap.c delete mode 100644 profiles/health/mcap.h delete mode 100644 profiles/health/mcap_internal.h delete mode 100644 profiles/health/mcap_lib.h delete mode 100644 profiles/health/mcap_sync.c diff --git a/Makefile.plugins b/Makefile.plugins index e615450..7fd0e50 100644 --- a/Makefile.plugins +++ b/Makefile.plugins @@ -64,9 +64,7 @@ builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \ if EXPERIMENTAL builtin_modules += health -builtin_sources += profiles/health/mcap_lib.h profiles/health/mcap_internal.h \ - profiles/health/mcap.h profiles/health/mcap.c \ - profiles/health/mcap_sync.c \ +builtin_sources += profiles/health/mcap-lib.h profiles/health/mcap-lib.c \ profiles/health/hdp_main.c profiles/health/hdp_types.h \ profiles/health/hdp_manager.h \ profiles/health/hdp_manager.c \ diff --git a/android/Android.mk b/android/Android.mk index 065ae1e..3ace786 100644 --- a/android/Android.mk +++ b/android/Android.mk @@ -56,7 +56,7 @@ LOCAL_SRC_FILES := \ bluez/android/handsfree-client.c \ bluez/android/gatt.c \ bluez/android/health.c \ - bluez/android/mcap-lib.c \ + bluez/profiles/health/mcap-lib.c \ bluez/src/log.c \ bluez/src/shared/mgmt.c \ bluez/src/shared/util.c \ @@ -211,7 +211,7 @@ LOCAL_SRC_FILES := \ bluez/btio/btio.c \ bluez/lib/bluetooth.c \ bluez/lib/hci.c \ - bluez/android/mcap-lib.c \ + bluez/profiles/health/mcap-lib.c \ bluez/android/mcaptest.c \ LOCAL_C_INCLUDES := \ diff --git a/android/Makefile.am b/android/Makefile.am index 5a8a452..4953531 100644 --- a/android/Makefile.am +++ b/android/Makefile.am @@ -45,7 +45,7 @@ android_bluetoothd_SOURCES = android/main.c \ android/handsfree-client.c android/handsfree-client.h \ android/gatt.h android/gatt.c \ android/health.h android/health.c \ - android/mcap-lib.h android/mcap-lib.c \ + profiles/health/mcap-lib.h profiles/health/mcap-lib.c \ attrib/att.c attrib/att.h \ attrib/gatt.c attrib/gatt.h \ attrib/gattrib.c attrib/gattrib.h \ @@ -95,7 +95,7 @@ noinst_PROGRAMS += android/mcaptest android_mcaptest_SOURCES = android/mcaptest.c \ src/log.h src/log.c \ btio/btio.h btio/btio.c \ - android/mcap-lib.h android/mcap-lib.c + profiles/health/mcap-lib.h profiles/health/mcap-lib.c android_mcaptest_CFLAGS = $(AM_CFLAGS) android_mcaptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ diff --git a/android/health.c b/android/health.c index fb2a1e3..5d161e0 100644 --- a/android/health.c +++ b/android/health.c @@ -43,6 +43,7 @@ #include "src/shared/queue.h" #include "src/uuid-helper.h" #include "src/sdp-client.h" +#include "profiles/health/mcap-lib.h" #include "hal-msg.h" #include "ipc-common.h" @@ -50,7 +51,6 @@ #include "utils.h" #include "bluetooth.h" #include "health.h" -#include "mcap-lib.h" #define SVC_HINT_HEALTH 0x00 #define HDP_VERSION 0x0101 diff --git a/android/mcaptest.c b/android/mcaptest.c index f727a79..638220b 100644 --- a/android/mcaptest.c +++ b/android/mcaptest.c @@ -38,7 +38,7 @@ #include "btio/btio.h" #include "lib/l2cap.h" -#include "android/mcap-lib.h" +#include "profiles/health/mcap-lib.h" enum { MODE_NONE, diff --git a/profiles/health/hdp.c b/profiles/health/hdp.c index 8ffcd91..d2e634a 100644 --- a/profiles/health/hdp.c +++ b/profiles/health/hdp.c @@ -42,11 +42,10 @@ #include "src/sdpd.h" #include "btio/btio.h" -#include "mcap_lib.h" #include "hdp_types.h" #include "hdp_util.h" #include "hdp.h" -#include "mcap.h" +#include "mcap-lib.h" #define ECHO_TIMEOUT 1 /* second */ #define HDP_ECHO_LEN 15 diff --git a/profiles/health/hdp_util.c b/profiles/health/hdp_util.c index e3e0830..c524054 100644 --- a/profiles/health/hdp_util.c +++ b/profiles/health/hdp_util.c @@ -48,8 +48,7 @@ #include "src/log.h" #include "src/dbus-common.h" -#include "mcap.h" -#include "mcap_lib.h" +#include "mcap-lib.h" #include "hdp_types.h" #include "hdp.h" #include "hdp_util.h" diff --git a/android/mcap-lib.c b/profiles/health/mcap-lib.c similarity index 100% rename from android/mcap-lib.c rename to profiles/health/mcap-lib.c diff --git a/android/mcap-lib.h b/profiles/health/mcap-lib.h similarity index 100% rename from android/mcap-lib.h rename to profiles/health/mcap-lib.h diff --git a/profiles/health/mcap.c b/profiles/health/mcap.c deleted file mode 100644 index 102ec85..0000000 --- a/profiles/health/mcap.c +++ /dev/null @@ -1,2192 +0,0 @@ -/* - * - * MCAP for BlueZ - Bluetooth protocol stack for Linux - * - * 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 - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <netinet/in.h> -#include <stdlib.h> -#include <errno.h> -#include <unistd.h> - -#include <glib.h> - -#include <bluetooth/bluetooth.h> -#include <bluetooth/l2cap.h> - -#include "btio/btio.h" -#include "src/log.h" -#include "src/error.h" - -#include "mcap.h" -#include "mcap_lib.h" -#include "mcap_internal.h" - -#define RESPONSE_TIMER 6 /* seconds */ -#define MAX_CACHED 10 /* 10 devices */ - -#define MCAP_ERROR g_quark_from_static_string("mcap-error-quark") - -#define RELEASE_TIMER(__mcl) do { \ - if (__mcl->tid) { \ - g_source_remove(__mcl->tid); \ - __mcl->tid = 0; \ - } \ -} while(0) - -struct connect_mcl { - struct mcap_mcl *mcl; /* MCL for this operation */ - mcap_mcl_connect_cb connect_cb; /* Connect callback */ - GDestroyNotify destroy; /* Destroy callback */ - gpointer user_data; /* Callback user data */ -}; - -typedef union { - mcap_mdl_operation_cb op; - mcap_mdl_operation_conf_cb op_conf; - mcap_mdl_notify_cb notify; -} mcap_cb_type; - -struct mcap_mdl_op_cb { - struct mcap_mdl *mdl; /* MDL for this operation */ - mcap_cb_type cb; /* Operation callback */ - GDestroyNotify destroy; /* Destroy callback */ - gpointer user_data; /* Callback user data */ -}; - -/* MCAP finite state machine functions */ -static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l); -static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l); -static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l); - -static void (*proc_req[])(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) = { - proc_req_connected, - proc_req_pending, - proc_req_active -}; - -static void mcap_cache_mcl(struct mcap_mcl *mcl); - -static void default_mdl_connected_cb(struct mcap_mdl *mdl, gpointer data) -{ - DBG("MCAP Unmanaged mdl connection"); -} - -static void default_mdl_closed_cb(struct mcap_mdl *mdl, gpointer data) -{ - DBG("MCAP Unmanaged mdl closed"); -} - -static void default_mdl_deleted_cb(struct mcap_mdl *mdl, gpointer data) -{ - DBG("MCAP Unmanaged mdl deleted"); -} - -static void default_mdl_aborted_cb(struct mcap_mdl *mdl, gpointer data) -{ - DBG("MCAP Unmanaged mdl aborted"); -} - -static uint8_t default_mdl_conn_req_cb(struct mcap_mcl *mcl, - uint8_t mdepid, uint16_t mdlid, - uint8_t *conf, gpointer data) -{ - DBG("MCAP mdl remote connection aborted"); - /* Due to this callback isn't 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) -{ - DBG("MCAP mdl remote reconnection aborted"); - /* Due to this callback isn't managed this request won't be supported */ - return MCAP_REQUEST_NOT_SUPPORTED; -} - -static void set_default_cb(struct mcap_mcl *mcl) -{ - if (!mcl->cb) - mcl->cb = g_new0(struct mcap_mdl_cb, 1); - - 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_aborted = default_mdl_aborted_cb; - mcl->cb->mdl_conn_req = default_mdl_conn_req_cb; - mcl->cb->mdl_reconn_req = default_mdl_reconn_req_cb; -} - -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 "Invalid 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_UNSPECIFIED_ERROR: - return "Unspecified Error"; - case MCAP_REQUEST_NOT_SUPPORTED: - return "Request Not Supported"; - case MCAP_CONFIGURATION_REJECTED: - return "Configuration Rejected"; - default: - return "Unknown Response Code"; - } -} - -static gboolean mcap_send_std_opcode(struct mcap_mcl *mcl, void *cmd, - uint32_t size, GError **err) -{ - if (mcl->state == MCL_IDLE) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, - "MCL is not connected"); - return FALSE; - } - - if (mcl->req != MCL_AVAILABLE) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_RESOURCE_UNAVAILABLE, - "Pending request"); - return FALSE; - } - - if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_REQUEST_NOT_SUPPORTED, - "Remote does not support standard opcodes"); - return FALSE; - } - - if (mcl->state == MCL_PENDING) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_OPERATION, - "Not Std Op. Codes can be sent in PENDING State"); - return FALSE; - } - - if (mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), cmd, size) < 0) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, - "Command can't be sent, write error"); - return FALSE; - } - - mcl->lcmd = cmd; - mcl->req = MCL_WAITING_RSP; - - return TRUE; -} - -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; - - if (mdl->state == MDL_CONNECTED) { - mcl->state = MCL_ACTIVE; - return; - } - } - - mcl->state = MCL_CONNECTED; -} - -static void shutdown_mdl(struct mcap_mdl *mdl) -{ - mdl->state = MDL_CLOSED; - - if (mdl->wid) { - g_source_remove(mdl->wid); - mdl->wid = 0; - } - - if (mdl->dc) { - g_io_channel_shutdown(mdl->dc, TRUE, NULL); - g_io_channel_unref(mdl->dc); - mdl->dc = NULL; - } -} - -static void free_mdl(struct mcap_mdl *mdl) -{ - if (!mdl) - return; - - mcap_mcl_unref(mdl->mcl); - g_free(mdl); -} - -static int cmp_mdl_state(gconstpointer a, gconstpointer b) -{ - const struct mcap_mdl *mdl = a; - const MDLState *st = b; - - if (mdl->state == *st) - return 0; - else if (mdl->state < *st) - return -1; - else - return 1; -} - -static void free_mcap_mdl_op(struct mcap_mdl_op_cb *op) -{ - if (op->destroy) - op->destroy(op->user_data); - - if (op->mdl) - mcap_mdl_unref(op->mdl); - - g_free(op); -} - -static void free_mcl_priv_data(struct mcap_mcl *mcl) -{ - free_mcap_mdl_op(mcl->priv_data); - mcl->priv_data = NULL; -} - -static void mcap_notify_error(struct mcap_mcl *mcl, GError *err) -{ - struct mcap_mdl_op_cb *con = mcl->priv_data; - struct mcap_mdl *mdl; - MDLState st; - GSList *l; - - if (!con || !mcl->lcmd) - return; - - switch (mcl->lcmd[0]) { - case MCAP_MD_CREATE_MDL_REQ: - st = MDL_WAITING; - l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state); - mdl = l->data; - mcl->mdls = g_slist_remove(mcl->mdls, mdl); - mcap_mdl_unref(mdl); - update_mcl_state(mcl); - con->cb.op_conf(NULL, 0, err, con->user_data); - break; - case MCAP_MD_ABORT_MDL_REQ: - st = MDL_WAITING; - l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state); - shutdown_mdl(l->data); - update_mcl_state(mcl); - con->cb.notify(err, con->user_data); - break; - case MCAP_MD_DELETE_MDL_REQ: - for (l = mcl->mdls; l; l = l->next) { - mdl = l->data; - if (mdl->state == MDL_DELETING) - mdl->state = (mdl->dc) ? MDL_CONNECTED : - MDL_CLOSED; - } - update_mcl_state(mcl); - con->cb.notify(err, con->user_data); - break; - case MCAP_MD_RECONNECT_MDL_REQ: - st = MDL_WAITING; - l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state); - shutdown_mdl(l->data); - update_mcl_state(mcl); - con->cb.op(NULL, err, con->user_data); - break; - } - - free_mcl_priv_data(mcl); - g_free(mcl->lcmd); - mcl->lcmd = NULL; -} - -int mcap_send_data(int sock, const void *buf, uint32_t size) -{ - const uint8_t *buf_b = buf; - uint32_t sent = 0; - - while (sent < size) { - int n = write(sock, buf_b + sent, size - sent); - if (n < 0) - return -1; - sent += n; - } - - return 0; -} - -static int mcap_send_cmd(struct mcap_mcl *mcl, uint8_t oc, uint8_t rc, - uint16_t mdl, uint8_t *data, size_t len) -{ - mcap_rsp *cmd; - int sock, sent; - - if (mcl->cc == NULL) - return -1; - - sock = g_io_channel_unix_get_fd(mcl->cc); - - cmd = g_malloc(sizeof(mcap_rsp) + len); - cmd->op = oc; - cmd->rc = rc; - cmd->mdl = htons(mdl); - - if (data && len > 0) - memcpy(cmd->data, data, len); - - sent = mcap_send_data(sock, cmd, sizeof(mcap_rsp) + len); - g_free(cmd); - - return sent; -} - -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; -} - -static uint16_t generate_mdlid(struct mcap_mcl *mcl) -{ - uint16_t mdlid = mcl->next_mdl; - struct mcap_mdl *mdl; - - do { - mdl = get_mdl(mcl, mdlid); - if (!mdl) { - mcl->next_mdl = (mdlid % MCAP_MDLID_FINAL) + 1; - return mdlid; - } else - mdlid = (mdlid % MCAP_MDLID_FINAL) + 1; - } while (mdlid != mcl->next_mdl); - - /* No more mdlids availables */ - return 0; -} - -static mcap_md_req *create_req(uint8_t op, uint16_t mdl_id) -{ - mcap_md_req *req_cmd; - - req_cmd = g_new0(mcap_md_req, 1); - - req_cmd->op = op; - req_cmd->mdl = htons(mdl_id); - - return req_cmd; -} - -static mcap_md_create_mdl_req *create_mdl_req(uint16_t mdl_id, uint8_t mdep, - uint8_t conf) -{ - mcap_md_create_mdl_req *req_mdl; - - req_mdl = g_new0(mcap_md_create_mdl_req, 1); - - req_mdl->op = MCAP_MD_CREATE_MDL_REQ; - req_mdl->mdl = htons(mdl_id); - req_mdl->mdep = mdep; - req_mdl->conf = conf; - - return req_mdl; -} - -static int 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; - - GError *gerr = NULL; - - RELEASE_TIMER(mcl); - - g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED, - "Timeout waiting response"); - - mcap_notify_error(mcl, gerr); - - g_error_free(gerr); - mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data); - mcap_cache_mcl(mcl); - - return FALSE; -} - -gboolean mcap_create_mdl(struct mcap_mcl *mcl, - uint8_t mdepid, - uint8_t conf, - mcap_mdl_operation_conf_cb connect_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err) -{ - struct mcap_mdl *mdl; - struct mcap_mdl_op_cb *con; - mcap_md_create_mdl_req *cmd; - uint16_t id; - - id = generate_mdlid(mcl); - if (!id) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, - "Not more mdlids available"); - return FALSE; - } - - mdl = g_new0(struct mcap_mdl, 1); - mdl->mcl = mcap_mcl_ref(mcl); - mdl->mdlid = id; - mdl->mdep_id = mdepid; - mdl->state = MDL_WAITING; - - con = g_new0(struct mcap_mdl_op_cb, 1); - con->mdl = mcap_mdl_ref(mdl); - con->cb.op_conf = connect_cb; - con->destroy = destroy; - con->user_data = user_data; - - cmd = create_mdl_req(id, mdepid, conf); - if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_create_mdl_req), - err)) { - mcap_mdl_unref(con->mdl); - g_free(con); - g_free(cmd); - return FALSE; - } - - mcl->state = MCL_ACTIVE; - mcl->priv_data = con; - - mcl->mdls = g_slist_insert_sorted(mcl->mdls, mcap_mdl_ref(mdl), - compare_mdl); - mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, - mcl); - return TRUE; -} - -gboolean mcap_reconnect_mdl(struct mcap_mdl *mdl, - mcap_mdl_operation_cb reconnect_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err) -{ - struct mcap_mdl_op_cb *con; - struct mcap_mcl *mcl = mdl->mcl; - mcap_md_req *cmd; - - if (mdl->state != MDL_CLOSED) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, - "MDL is not closed"); - return FALSE; - } - - cmd = create_req(MCAP_MD_RECONNECT_MDL_REQ, mdl->mdlid); - if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) { - g_free(cmd); - return FALSE; - } - - mdl->state = MDL_WAITING; - - con = g_new0(struct mcap_mdl_op_cb, 1); - con->mdl = mcap_mdl_ref(mdl); - con->cb.op = reconnect_cb; - con->destroy = destroy; - con->user_data = user_data; - - mcl->state = MCL_ACTIVE; - mcl->priv_data = con; - - mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, - mcl); - return TRUE; -} - -static gboolean send_delete_req(struct mcap_mcl *mcl, - struct mcap_mdl_op_cb *con, - uint16_t mdlid, - GError **err) -{ - mcap_md_req *cmd; - - cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdlid); - if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) { - g_free(cmd); - return FALSE; - } - - mcl->priv_data = con; - - mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, - mcl); - return TRUE; -} - -gboolean mcap_delete_all_mdls(struct mcap_mcl *mcl, - mcap_mdl_notify_cb delete_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err) -{ - GSList *l; - struct mcap_mdl *mdl; - struct mcap_mdl_op_cb *con; - - DBG("MCL in state: %d", mcl->state); - if (!mcl->mdls) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, - "There are not MDLs created"); - return FALSE; - } - - 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.notify = delete_cb; - con->destroy = destroy; - con->user_data = user_data; - - - if (!send_delete_req(mcl, con, MCAP_ALL_MDLIDS, err)) { - g_free(con); - return FALSE; - } - - return TRUE; -} - -gboolean mcap_delete_mdl(struct mcap_mdl *mdl, mcap_mdl_notify_cb delete_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err) -{ - struct mcap_mcl *mcl= mdl->mcl; - struct mcap_mdl_op_cb *con; - GSList *l; - - l = g_slist_find(mcl->mdls, mdl); - - if (!l) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL, - "%s" , error2str(MCAP_INVALID_MDEP)); - return FALSE; - } - - if (mdl->state == MDL_WAITING) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, - "Mdl is not created"); - return FALSE; - } - - mdl->state = MDL_DELETING; - - con = g_new0(struct mcap_mdl_op_cb, 1); - con->mdl = mcap_mdl_ref(mdl); - con->cb.notify = delete_cb; - con->destroy = destroy; - con->user_data = user_data; - - if (!send_delete_req(mcl, con, mdl->mdlid, err)) { - mcap_mdl_unref(con->mdl); - g_free(con); - return FALSE; - } - - return TRUE; -} - -gboolean mcap_mdl_abort(struct mcap_mdl *mdl, mcap_mdl_notify_cb abort_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err) -{ - struct mcap_mdl_op_cb *con; - struct mcap_mcl *mcl = mdl->mcl; - mcap_md_req *cmd; - - if (mdl->state != MDL_WAITING) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, - "Mdl in invalid state"); - return FALSE; - } - - cmd = create_req(MCAP_MD_ABORT_MDL_REQ, mdl->mdlid); - if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) { - g_free(cmd); - return FALSE; - } - - con = g_new0(struct mcap_mdl_op_cb, 1); - con->mdl = mcap_mdl_ref(mdl); - con->cb.notify = abort_cb; - con->destroy = destroy; - con->user_data = user_data; - - mcl->priv_data = con; - mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, - mcl); - return TRUE; -} - -static struct mcap_mcl *find_mcl(GSList *list, const bdaddr_t *addr) -{ - struct mcap_mcl *mcl; - - for (; list; list = list->next) { - mcl = list->data; - - if (!bacmp(&mcl->addr, addr)) - return mcl; - } - - return NULL; -} - -int mcap_mdl_get_fd(struct mcap_mdl *mdl) -{ - if (!mdl || mdl->state != MDL_CONNECTED) - return -ENOTCONN; - - return g_io_channel_unix_get_fd(mdl->dc); -} - -uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl) -{ - if (!mdl) - return MCAP_MDLID_RESERVED; - - return mdl->mdlid; -} - -static void close_mcl(struct mcap_mcl *mcl, gboolean cache_requested) -{ - gboolean save = ((!(mcl->ctrl & MCAP_CTRL_FREE)) && cache_requested); - - RELEASE_TIMER(mcl); - - if (mcl->cc) { - g_io_channel_shutdown(mcl->cc, TRUE, NULL); - g_io_channel_unref(mcl->cc); - mcl->cc = NULL; - } - - if (mcl->wid) { - g_source_remove(mcl->wid); - mcl->wid = 0; - } - - if (mcl->lcmd) { - g_free(mcl->lcmd); - mcl->lcmd = NULL; - } - - if (mcl->priv_data) - free_mcl_priv_data(mcl); - - g_slist_foreach(mcl->mdls, (GFunc) shutdown_mdl, NULL); - - mcap_sync_stop(mcl); - - mcl->state = MCL_IDLE; - - if (save) - return; - - g_slist_foreach(mcl->mdls, (GFunc) mcap_mdl_unref, NULL); - g_slist_free(mcl->mdls); - mcl->mdls = NULL; -} - -static void mcap_mcl_shutdown(struct mcap_mcl *mcl) -{ - close_mcl(mcl, TRUE); -} - -static void mcap_mcl_release(struct mcap_mcl *mcl) -{ - close_mcl(mcl, FALSE); -} - -static void mcap_cache_mcl(struct mcap_mcl *mcl) -{ - GSList *l; - struct mcap_mcl *last; - int len; - - if (mcl->ctrl & MCAP_CTRL_CACHED) - return; - - mcl->mi->mcls = g_slist_remove(mcl->mi->mcls, mcl); - - if (mcl->ctrl & MCAP_CTRL_NOCACHE) { - mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl); - mcap_mcl_release(mcl); - mcap_mcl_unref(mcl); - return; - } - - DBG("Caching MCL"); - - len = g_slist_length(mcl->mi->cached); - if (len == MAX_CACHED) { - /* Remove the latest cached mcl */ - l = g_slist_last(mcl->mi->cached); - last = l->data; - mcl->mi->cached = g_slist_remove(mcl->mi->cached, last); - last->ctrl &= ~MCAP_CTRL_CACHED; - if (last->ctrl & MCAP_CTRL_CONN) { - /* We have to release this MCL if */ - /* connection is not successful */ - last->ctrl |= MCAP_CTRL_FREE; - } else { - mcap_mcl_release(last); - last->mi->mcl_uncached_cb(last, last->mi->user_data); - } - mcap_mcl_unref(last); - } - - mcl->mi->cached = g_slist_prepend(mcl->mi->cached, mcl); - mcl->ctrl |= MCAP_CTRL_CACHED; - mcap_mcl_shutdown(mcl); -} - -static void mcap_uncache_mcl(struct mcap_mcl *mcl) -{ - if (!(mcl->ctrl & MCAP_CTRL_CACHED)) - return; - - DBG("Got MCL from cache"); - - mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl); - mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls, mcl); - mcl->ctrl &= ~MCAP_CTRL_CACHED; - mcl->ctrl &= ~MCAP_CTRL_FREE; -} - -void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache) -{ - if (!mcl) - return; - - if (mcl->ctrl & MCAP_CTRL_FREE) { - mcap_mcl_release(mcl); - return; - } - - if (!cache) - mcl->ctrl |= MCAP_CTRL_NOCACHE; - - if (mcl->cc) { - g_io_channel_shutdown(mcl->cc, TRUE, NULL); - g_io_channel_unref(mcl->cc); - mcl->cc = NULL; - mcl->state = MCL_IDLE; - } else if ((mcl->ctrl & MCAP_CTRL_CACHED) && - (mcl->ctrl & MCAP_CTRL_NOCACHE)) { - mcl->ctrl &= ~MCAP_CTRL_CACHED; - mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl); - mcap_mcl_release(mcl); - mcap_mcl_unref(mcl); - } -} - -struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl) -{ - mcl->ref++; - - DBG("mcap_mcl_ref(%p): ref=%d", mcl, mcl->ref); - - return mcl; -} - -void mcap_mcl_unref(struct mcap_mcl *mcl) -{ - mcl->ref--; - - DBG("mcap_mcl_unref(%p): ref=%d", mcl, mcl->ref); - - if (mcl->ref > 0) - return; - - mcap_mcl_release(mcl); - mcap_instance_unref(mcl->mi); - g_free(mcl->cb); - g_free(mcl); -} - -static gboolean parse_set_opts(struct mcap_mdl_cb *mdl_cb, GError **err, - McapMclCb cb1, va_list args) -{ - McapMclCb cb = cb1; - struct mcap_mdl_cb *c; - - c = g_new0(struct mcap_mdl_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_ABORTED: - c->mdl_aborted = 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); - g_free(c); - return FALSE; - } - cb = va_arg(args, int); - } - - /* Set new callbacks */ - if (c->mdl_connected) - mdl_cb->mdl_connected = c->mdl_connected; - if (c->mdl_closed) - mdl_cb->mdl_closed = c->mdl_closed; - if (c->mdl_deleted) - mdl_cb->mdl_deleted = c->mdl_deleted; - if (c->mdl_aborted) - mdl_cb->mdl_aborted = c->mdl_aborted; - if (c->mdl_conn_req) - mdl_cb->mdl_conn_req = c->mdl_conn_req; - if (c->mdl_reconn_req) - mdl_cb->mdl_reconn_req = c->mdl_reconn_req; - - g_free(c); - - return TRUE; -} - -gboolean mcap_mcl_set_cb(struct mcap_mcl *mcl, gpointer user_data, - GError **gerr, 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 FALSE; - - mcl->cb->user_data = user_data; - return TRUE; -} - -void mcap_mcl_get_addr(struct mcap_mcl *mcl, bdaddr_t *addr) -{ - bacpy(addr, &mcl->addr); -} - -static void mcap_del_mdl(gpointer elem, gpointer user_data) -{ - struct mcap_mdl *mdl = elem; - gboolean notify = *(gboolean *) user_data; - - shutdown_mdl(mdl); - if (notify) - mdl->mcl->cb->mdl_deleted(mdl, mdl->mcl->cb->user_data); - - mcap_mdl_unref(mdl); -} - -static gboolean check_cmd_req_length(struct mcap_mcl *mcl, void *cmd, - uint32_t rlen, uint32_t explen, uint8_t rspcod) -{ - mcap_md_req *req; - uint16_t mdl_id; - - if (rlen != explen) { - if (rlen >= sizeof(mcap_md_req)) { - req = cmd; - mdl_id = ntohs(req->mdl); - } else { - /* We can't get mdlid */ - mdl_id = MCAP_MDLID_RESERVED; - } - mcap_send_cmd(mcl, rspcod, MCAP_INVALID_PARAM_VALUE, mdl_id, - NULL, 0); - return FALSE; - } - return TRUE; -} - -static void process_md_create_mdl_req(struct mcap_mcl *mcl, void *cmd, - uint32_t 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 (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_create_mdl_req), - MCAP_MD_CREATE_MDL_RSP)) - return; - - req = cmd; - mdl_id = ntohs(req->mdl); - if (mdl_id < MCAP_MDLID_INITIAL || mdl_id > MCAP_MDLID_FINAL) { - mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDL, - mdl_id, NULL, 0); - return; - } - - mdep_id = req->mdep; - if (mdep_id > MCAP_MDEPID_FINAL) { - mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDEP, - mdl_id, NULL, 0); - return; - } - - mdl = get_mdl(mcl, mdl_id); - if (mdl && (mdl->state == MDL_WAITING || mdl->state == MDL_DELETING )) { - /* Creation request arrives for a MDL that is being managed - * at current moment */ - mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_MDL_BUSY, - mdl_id, NULL, 0); - 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 (mcl->state == MCL_IDLE) { - /* MCL has been closed int the callback */ - return; - } - - if (cfga != 0 && cfga != conf) { - /* Remote device set default configuration but upper profile */ - /* has changed it. Protocol Error: force closing the MCL by */ - /* remote device using UNSPECIFIED_ERROR response */ - mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, - MCAP_UNSPECIFIED_ERROR, mdl_id, NULL, 0); - return; - } - if (rsp != MCAP_SUCCESS) { - mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, rsp, mdl_id, - NULL, 0); - return; - } - - if (!mdl) { - mdl = g_new0(struct mcap_mdl, 1); - mdl->mcl = mcap_mcl_ref(mcl); - mdl->mdlid = mdl_id; - mcl->mdls = g_slist_insert_sorted(mcl->mdls, mcap_mdl_ref(mdl), - compare_mdl); - } else if (mdl->state == MDL_CONNECTED) { - /* MCAP specification says that we should close the MCL if - * it is open when we receive a MD_CREATE_MDL_REQ */ - shutdown_mdl(mdl); - } - - mdl->mdep_id = mdep_id; - mdl->state = MDL_WAITING; - - mcl->state = MCL_PENDING; - mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_SUCCESS, mdl_id, - &conf, 1); -} - -static void process_md_reconnect_mdl_req(struct mcap_mcl *mcl, void *cmd, - uint32_t len) -{ - mcap_md_req *req; - struct mcap_mdl *mdl; - uint16_t mdl_id; - uint8_t rsp; - - if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req), - MCAP_MD_RECONNECT_MDL_RSP)) - return; - - req = cmd; - mdl_id = ntohs(req->mdl); - - mdl = get_mdl(mcl, mdl_id); - if (!mdl) { - mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_INVALID_MDL, - mdl_id, NULL, 0); - return; - } else if (mdl->state == MDL_WAITING || mdl->state == MDL_DELETING ) { - /* Creation request arrives for a MDL that is being managed - * at current moment */ - mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_MDL_BUSY, - mdl_id, NULL, 0); - return; - } - - /* Callback to upper layer */ - rsp = mcl->cb->mdl_reconn_req(mdl, mcl->cb->user_data); - if (mcl->state == MCL_IDLE) - return; - - if (rsp != MCAP_SUCCESS) { - mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, rsp, mdl_id, - NULL, 0); - return; - } - - if (mdl->state == MDL_CONNECTED) - shutdown_mdl(mdl); - - mdl->state = MDL_WAITING; - mcl->state = MCL_PENDING; - mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_SUCCESS, mdl_id, - NULL, 0); -} - -static void process_md_abort_mdl_req(struct mcap_mcl *mcl, void *cmd, - uint32_t len) -{ - mcap_md_req *req; - GSList *l; - struct mcap_mdl *mdl, *abrt; - uint16_t mdl_id; - - if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req), - MCAP_MD_ABORT_MDL_RSP)) - return; - - req = cmd; - mdl_id = ntohs(req->mdl); - mcl->state = MCL_CONNECTED; - abrt = NULL; - for (l = mcl->mdls; l; l = l->next) { - mdl = l->data; - if (mdl_id == mdl->mdlid && mdl->state == MDL_WAITING) { - abrt = mdl; - if (mcl->state != MCL_CONNECTED) - break; - continue; - } - if (mdl->state == MDL_CONNECTED && mcl->state != MCL_ACTIVE) - mcl->state = MCL_ACTIVE; - - if (abrt && mcl->state == MCL_ACTIVE) - break; - } - - if (!abrt) { - mcap_send_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_INVALID_MDL, - mdl_id, NULL, 0); - return; - } - - mcl->cb->mdl_aborted(abrt, mcl->cb->user_data); - abrt->state = MDL_CLOSED; - mcap_send_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_SUCCESS, mdl_id, - NULL, 0); -} - -static void process_md_delete_mdl_req(struct mcap_mcl *mcl, void *cmd, - uint32_t len) -{ - mcap_md_req *req; - struct mcap_mdl *mdl, *aux; - uint16_t mdlid; - gboolean notify; - GSList *l; - - if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req), - MCAP_MD_DELETE_MDL_RSP)) - return; - - req = cmd; - mdlid = ntohs(req->mdl); - if (mdlid == MCAP_ALL_MDLIDS) { - notify = FALSE; - g_slist_foreach(mcl->mdls, mcap_del_mdl, ¬ify); - g_slist_free(mcl->mdls); - mcl->mdls = NULL; - mcl->state = MCL_CONNECTED; - /* 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) { - mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL, - mdlid, NULL, 0); - return; - } - - for (l = mcl->mdls, mdl = NULL; l; l = l->next) { - aux = l->data; - if (aux->mdlid == mdlid) { - mdl = aux; - break; - } - } - - if (!mdl || mdl->state == MDL_WAITING) { - mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL, - mdlid, NULL, 0); - return; - } - - mcl->mdls = g_slist_remove(mcl->mdls, mdl); - update_mcl_state(mcl); - notify = TRUE; - mcap_del_mdl(mdl, ¬ify); - -resp: - mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_SUCCESS, mdlid, - NULL, 0); -} - -static void invalid_req_state(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) -{ - uint16_t mdlr; - - error("Invalid cmd received (op code = %d) in state %d", cmd[0], - mcl->state); - /* Get previously mdlid sent to generate an appropriate - * response if it is possible */ - mdlr = len < sizeof(mcap_md_req) ? MCAP_MDLID_RESERVED : - ntohs(((mcap_md_req *) cmd)->mdl); - mcap_send_cmd(mcl, cmd[0]+1, MCAP_INVALID_OPERATION, mdlr, NULL, 0); -} - -/* Function used to process commands depending of MCL state */ -static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) -{ - 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, cmd, len); - break; - default: - invalid_req_state(mcl, cmd, len); - } -} - -static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) -{ - if (cmd[0] == MCAP_MD_ABORT_MDL_REQ) - process_md_abort_mdl_req(mcl, cmd, len); - else - invalid_req_state(mcl, cmd, len); -} - -static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) -{ - 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, cmd, len); - break; - default: - invalid_req_state(mcl, cmd, len); - } -} - -/* Function used to process replies */ -static gboolean check_err_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp, - uint32_t rlen, uint32_t len, GError **gerr) -{ - mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd; - int err = MCAP_ERROR_FAILED; - gboolean close = FALSE; - char *msg; - - if (rsp->op == MCAP_ERROR_RSP) { - msg = "MCAP_ERROR_RSP received"; - close = FALSE; - goto fail; - } - - /* Check if the response matches with the last request */ - if (rlen < sizeof(mcap_rsp) || (mcl->lcmd[0] + 1) != rsp->op) { - msg = "Protocol error"; - close = FALSE; - goto fail; - } - - if (rlen < len) { - msg = "Protocol error"; - close = FALSE; - goto fail; - } - - if (rsp->mdl != cmdlast->mdl) { - msg = "MDLID received doesn't match with MDLID sent"; - close = TRUE; - goto fail; - } - - if (rsp->rc == MCAP_REQUEST_NOT_SUPPORTED) { - msg = "Remote does not support opcodes"; - mcl->ctrl &= ~MCAP_CTRL_STD_OP; - goto fail; - } - - if (rsp->rc == MCAP_UNSPECIFIED_ERROR) { - msg = "Unspecified error"; - close = TRUE; - goto fail; - } - - if (rsp->rc != MCAP_SUCCESS) { - msg = error2str(rsp->rc); - err = rsp->rc; - goto fail; - } - - return FALSE; - -fail: - g_set_error(gerr, MCAP_ERROR, err, "%s", msg); - return close; -} - -static gboolean process_md_create_mdl_rsp(struct mcap_mcl *mcl, - mcap_rsp *rsp, uint32_t len) -{ - mcap_md_create_mdl_req *cmdlast = (mcap_md_create_mdl_req *) mcl->lcmd; - struct mcap_mdl_op_cb *conn = mcl->priv_data; - mcap_mdl_operation_conf_cb connect_cb = conn->cb.op_conf; - gpointer user_data = conn->user_data; - struct mcap_mdl *mdl = conn->mdl; - uint8_t conf = cmdlast->conf; - gboolean close; - GError *gerr = NULL; - - close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp) + 1, &gerr); - g_free(mcl->lcmd); - mcl->lcmd = NULL; - mcl->req = MCL_AVAILABLE; - - if (gerr) - goto fail; - - /* Check if preferences changed */ - if (conf != 0x00 && rsp->data[0] != conf) { - g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED, - "Configuration changed"); - close = TRUE; - goto fail; - } - - connect_cb(mdl, rsp->data[0], gerr, user_data); - return close; - -fail: - connect_cb(NULL, 0, gerr, user_data); - mcl->mdls = g_slist_remove(mcl->mdls, mdl); - mcap_mdl_unref(mdl); - g_error_free(gerr); - update_mcl_state(mcl); - return close; -} - -static gboolean process_md_reconnect_mdl_rsp(struct mcap_mcl *mcl, - mcap_rsp *rsp, uint32_t len) -{ - struct mcap_mdl_op_cb *reconn = mcl->priv_data; - mcap_mdl_operation_cb reconn_cb = reconn->cb.op; - gpointer user_data = reconn->user_data; - struct mcap_mdl *mdl = reconn->mdl; - GError *gerr = NULL; - gboolean close; - - close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr); - - g_free(mcl->lcmd); - mcl->lcmd = NULL; - mcl->req = MCL_AVAILABLE; - - reconn_cb(mdl, gerr, user_data); - if (!gerr) - return close; - - g_error_free(gerr); - shutdown_mdl(mdl); - update_mcl_state(mcl); - - if (rsp->rc != MCAP_INVALID_MDL) - return close; - - /* Remove cached mdlid */ - mcl->mdls = g_slist_remove(mcl->mdls, mdl); - mcl->cb->mdl_deleted(mdl, mcl->cb->user_data); - mcap_mdl_unref(mdl); - - return close; -} - -static gboolean process_md_abort_mdl_rsp(struct mcap_mcl *mcl, - mcap_rsp *rsp, uint32_t len) -{ - struct mcap_mdl_op_cb *abrt = mcl->priv_data; - mcap_mdl_notify_cb abrt_cb = abrt->cb.notify; - gpointer user_data = abrt->user_data; - struct mcap_mdl *mdl = abrt->mdl; - GError *gerr = NULL; - gboolean close; - - close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr); - - g_free(mcl->lcmd); - mcl->lcmd = NULL; - mcl->req = MCL_AVAILABLE; - - abrt_cb(gerr, user_data); - shutdown_mdl(mdl); - - if (len >= sizeof(mcap_rsp) && rsp->rc == MCAP_INVALID_MDL) { - mcl->mdls = g_slist_remove(mcl->mdls, mdl); - mcl->cb->mdl_deleted(mdl, mcl->cb->user_data); - mcap_mdl_unref(mdl); - } - - if (gerr) - g_error_free(gerr); - - update_mcl_state(mcl); - - return close; -} - -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 void check_mdl_del_err(struct mcap_mdl *mdl, mcap_rsp *rsp) -{ - if (rsp->rc != MCAP_ERROR_INVALID_MDL) { - restore_mdl(mdl, NULL); - return; - } - - /* MDL does not exist in remote side, we can delete it */ - mdl->mcl->mdls = g_slist_remove(mdl->mcl->mdls, mdl); - mcap_mdl_unref(mdl); -} - -static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp, - uint32_t len) -{ - struct mcap_mdl_op_cb *del = mcl->priv_data; - struct mcap_mdl *mdl = del->mdl; - mcap_mdl_notify_cb deleted_cb = del->cb.notify; - gpointer user_data = del->user_data; - mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd; - uint16_t mdlid = ntohs(cmdlast->mdl); - GError *gerr = NULL; - gboolean close; - gboolean notify = FALSE; - - close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr); - - g_free(mcl->lcmd); - mcl->lcmd = NULL; - mcl->req = MCL_AVAILABLE; - - if (gerr) { - if (mdl) - check_mdl_del_err(mdl, rsp); - else - g_slist_foreach(mcl->mdls, restore_mdl, NULL); - deleted_cb(gerr, user_data); - g_error_free(gerr); - return close; - } - - if (mdlid == MCAP_ALL_MDLIDS) { - g_slist_foreach(mcl->mdls, mcap_del_mdl, ¬ify); - g_slist_free(mcl->mdls); - mcl->mdls = NULL; - mcl->state = MCL_CONNECTED; - } else { - mcl->mdls = g_slist_remove(mcl->mdls, mdl); - update_mcl_state(mcl); - mcap_del_mdl(mdl, ¬ify); - } - - deleted_cb(gerr, user_data); - - return close; -} - -static void post_process_rsp(struct mcap_mcl *mcl, struct mcap_mdl_op_cb *op) -{ - if (mcl->priv_data != op) { - /* Queued MCAP request in some callback. */ - /* We should not delete the mcl private data */ - free_mcap_mdl_op(op); - } else { - /* This is not a queued request. It's safe */ - /* delete the mcl private data here. */ - free_mcl_priv_data(mcl); - } -} - -static void proc_response(struct mcap_mcl *mcl, void *buf, uint32_t len) -{ - struct mcap_mdl_op_cb *op = mcl->priv_data; - mcap_rsp *rsp = buf; - gboolean close; - - RELEASE_TIMER(mcl); - - switch (mcl->lcmd[0] + 1) { - case MCAP_MD_CREATE_MDL_RSP: - close = process_md_create_mdl_rsp(mcl, rsp, len); - post_process_rsp(mcl, op); - break; - case MCAP_MD_RECONNECT_MDL_RSP: - close = process_md_reconnect_mdl_rsp(mcl, rsp, len); - post_process_rsp(mcl, op); - break; - case MCAP_MD_ABORT_MDL_RSP: - close = process_md_abort_mdl_rsp(mcl, rsp, len); - post_process_rsp(mcl, op); - break; - case MCAP_MD_DELETE_MDL_RSP: - close = process_md_delete_mdl_rsp(mcl, rsp, len); - post_process_rsp(mcl, op); - break; - default: - DBG("Unknown cmd response received (op code = %d)", rsp->op); - close = TRUE; - break; - } - - if (close) { - mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data); - mcap_cache_mcl(mcl); - } -} - -static void proc_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) -{ - GError *gerr = NULL; - - if (cmd[0] > MCAP_MD_SYNC_INFO_IND || - (cmd[0] > MCAP_MD_DELETE_MDL_RSP && - cmd[0] < MCAP_MD_SYNC_CAP_REQ)) { - error("Unknown cmd received (op code = %d)", cmd[0]); - mcap_send_cmd(mcl, MCAP_ERROR_RSP, MCAP_INVALID_OP_CODE, - MCAP_MDLID_RESERVED, NULL, 0); - return; - } - - if (cmd[0] >= MCAP_MD_SYNC_CAP_REQ && - cmd[0] <= MCAP_MD_SYNC_INFO_IND) { - proc_sync_cmd(mcl, cmd, len); - return; - } - - if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) { - /* In case the remote device doesn't work correctly */ - 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; - /* Initiator will ignore our last request */ - RELEASE_TIMER(mcl); - mcl->req = MCL_AVAILABLE; - g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_REQ_IGNORED, - "Initiator sent a request with more priority"); - mcap_notify_error(mcl, gerr); - proc_req[mcl->state](mcl, cmd, len); - return; - } - proc_response(mcl, cmd, len); - } else if (cmd[0] & 0x01) - proc_req[mcl->state](mcl, cmd, len); -} - -static gboolean mdl_event_cb(GIOChannel *chan, GIOCondition cond, gpointer data) -{ - - struct mcap_mdl *mdl = data; - gboolean notify; - - DBG("Close MDL %d", mdl->mdlid); - - notify = (mdl->state == MDL_CONNECTED); - shutdown_mdl(mdl); - - update_mcl_state(mdl->mcl); - - if (notify) { - /*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; - - DBG("mdl connect callback"); - - if (conn_err) { - DBG("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_full(mdl->dc, G_PRIORITY_DEFAULT, - G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) mdl_event_cb, - mcap_mdl_ref(mdl), - (GDestroyNotify) mcap_mdl_unref); - - cb(mdl, conn_err, user_data); -} - -gboolean mcap_connect_mdl(struct mcap_mdl *mdl, uint8_t mode, - uint16_t dcpsm, - mcap_mdl_operation_cb connect_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err) -{ - struct mcap_mdl_op_cb *con; - - if (mdl->state != MDL_WAITING) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL, - "%s", error2str(MCAP_INVALID_MDL)); - return FALSE; - } - - if ((mode != L2CAP_MODE_ERTM) && (mode != L2CAP_MODE_STREAMING)) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, - "Invalid MDL configuration"); - return FALSE; - } - - con = g_new0(struct mcap_mdl_op_cb, 1); - con->mdl = mcap_mdl_ref(mdl); - con->cb.op = connect_cb; - con->destroy = destroy; - con->user_data = user_data; - - mdl->dc = bt_io_connect(mcap_connect_mdl_cb, con, - (GDestroyNotify) free_mcap_mdl_op, err, - BT_IO_OPT_SOURCE_BDADDR, &mdl->mcl->mi->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->mi->sec, - BT_IO_OPT_MODE, mode, - BT_IO_OPT_INVALID); - if (!mdl->dc) { - DBG("MDL Connection error"); - mdl->state = MDL_CLOSED; - mcap_mdl_unref(con->mdl); - g_free(con); - return FALSE; - } - - return TRUE; -} - -static gboolean mcl_control_cb(GIOChannel *chan, GIOCondition cond, - gpointer data) -{ - GError *gerr = NULL; - struct mcap_mcl *mcl = data; - int sk, len; - uint8_t buf[MCAP_CC_MTU]; - - if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) - goto fail; - - sk = g_io_channel_unix_get_fd(chan); - len = read(sk, buf, sizeof(buf)); - if (len < 0) - goto fail; - - proc_cmd(mcl, buf, (uint32_t) len); - return TRUE; - -fail: - if (mcl->state != MCL_IDLE) { - if (mcl->req == MCL_WAITING_RSP) { - /* notify error in pending callback */ - g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_MCL_CLOSED, - "MCL closed"); - mcap_notify_error(mcl, gerr); - g_error_free(gerr); - } - mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data); - } - mcap_cache_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; - - mcl->ctrl &= ~MCAP_CTRL_CONN; - - if (conn_err) { - if (mcl->ctrl & MCAP_CTRL_FREE) { - mcap_mcl_release(mcl); - mcl->mi->mcl_uncached_cb(mcl, mcl->mi->user_data); - } - connect_cb(NULL, conn_err, data); - return; - } - - ba2str(&mcl->addr, dstaddr); - - aux = find_mcl(mcl->mi->mcls, &mcl->addr); - if (aux) { - /* Double MCL connection case */ - 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->ctrl |= MCAP_CTRL_STD_OP; - - mcap_sync_init(mcl); - - if (mcl->ctrl & MCAP_CTRL_CACHED) - mcap_uncache_mcl(mcl); - else { - mcl->ctrl &= ~MCAP_CTRL_FREE; - mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls, - mcap_mcl_ref(mcl)); - } - - mcl->wid = g_io_add_watch_full(mcl->cc, G_PRIORITY_DEFAULT, - G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) mcl_control_cb, - mcap_mcl_ref(mcl), - (GDestroyNotify) mcap_mcl_unref); - connect_cb(mcl, gerr, data); -} - -static void set_mdl_properties(GIOChannel *chan, struct mcap_mdl *mdl) -{ - struct mcap_mcl *mcl = mdl->mcl; - - mdl->state = MDL_CONNECTED; - mdl->dc = g_io_channel_ref(chan); - mdl->wid = g_io_add_watch_full(mdl->dc, G_PRIORITY_DEFAULT, - G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) mdl_event_cb, - mcap_mdl_ref(mdl), - (GDestroyNotify) mcap_mdl_unref); - - mcl->state = MCL_ACTIVE; - mcl->cb->mdl_connected(mdl, mcl->cb->user_data); -} - -static void mcl_io_destroy(gpointer data) -{ - struct connect_mcl *con = data; - - mcap_mcl_unref(con->mcl); - if (con->destroy) - con->destroy(con->user_data); - g_free(con); -} - -gboolean mcap_create_mcl(struct mcap_instance *mi, - const bdaddr_t *addr, - uint16_t ccpsm, - mcap_mcl_connect_cb connect_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err) -{ - struct mcap_mcl *mcl; - struct connect_mcl *con; - - mcl = find_mcl(mi->mcls, addr); - if (mcl) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS, - "MCL is already connected."); - return FALSE; - } - - mcl = find_mcl(mi->cached, addr); - if (!mcl) { - mcl = g_new0(struct mcap_mcl, 1); - mcl->mi = mcap_instance_ref(mi); - mcl->state = MCL_IDLE; - bacpy(&mcl->addr, addr); - set_default_cb(mcl); - mcl->next_mdl = (rand() % MCAP_MDLID_FINAL) + 1; - } - - mcl->ctrl |= MCAP_CTRL_CONN; - - con = g_new0(struct connect_mcl, 1); - con->mcl = mcap_mcl_ref(mcl); - con->connect_cb = connect_cb; - con->destroy = destroy; - con->user_data = user_data; - - mcl->cc = bt_io_connect(mcap_connect_mcl_cb, con, - mcl_io_destroy, err, - BT_IO_OPT_SOURCE_BDADDR, &mi->src, - BT_IO_OPT_DEST_BDADDR, addr, - BT_IO_OPT_PSM, ccpsm, - BT_IO_OPT_MTU, MCAP_CC_MTU, - BT_IO_OPT_SEC_LEVEL, mi->sec, - BT_IO_OPT_MODE, L2CAP_MODE_ERTM, - BT_IO_OPT_INVALID); - if (!mcl->cc) { - mcl->ctrl &= ~MCAP_CTRL_CONN; - if (mcl->ctrl & MCAP_CTRL_FREE) { - mcap_mcl_release(mcl); - mcl->mi->mcl_uncached_cb(mcl, mcl->mi->user_data); - } - mcap_mcl_unref(con->mcl); - g_free(con); - return FALSE; - } - - return TRUE; -} - -static void connect_dc_event_cb(GIOChannel *chan, GError *gerr, - gpointer user_data) -{ - struct mcap_instance *mi = user_data; - struct mcap_mcl *mcl; - struct mcap_mdl *mdl; - GError *err = NULL; - bdaddr_t dst; - GSList *l; - - if (gerr) - return; - - bt_io_get(chan, &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(mi->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) { - set_mdl_properties(chan, mdl); - return; - } - } - -drop: - g_io_channel_shutdown(chan, TRUE, NULL); -} - -static void set_mcl_conf(GIOChannel *chan, struct mcap_mcl *mcl) -{ - gboolean reconn; - - mcl->state = MCL_CONNECTED; - mcl->role = MCL_ACCEPTOR; - mcl->req = MCL_AVAILABLE; - mcl->cc = g_io_channel_ref(chan); - mcl->ctrl |= MCAP_CTRL_STD_OP; - - mcap_sync_init(mcl); - - reconn = (mcl->ctrl & MCAP_CTRL_CACHED); - if (reconn) - mcap_uncache_mcl(mcl); - else - mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls, - mcap_mcl_ref(mcl)); - - mcl->wid = g_io_add_watch_full(mcl->cc, G_PRIORITY_DEFAULT, - G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) mcl_control_cb, - mcap_mcl_ref(mcl), - (GDestroyNotify) mcap_mcl_unref); - - /* Callback to report new MCL */ - if (reconn) - mcl->mi->mcl_reconnected_cb(mcl, mcl->mi->user_data); - else - mcl->mi->mcl_connected_cb(mcl, mcl->mi->user_data); -} - -static void connect_mcl_event_cb(GIOChannel *chan, GError *gerr, - gpointer user_data) -{ - struct mcap_instance *mi = user_data; - struct mcap_mcl *mcl; - bdaddr_t dst; - char address[18], srcstr[18]; - GError *err = NULL; - - if (gerr) - return; - - bt_io_get(chan, &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(&mi->src, srcstr); - mcl = find_mcl(mi->mcls, &dst); - if (mcl) { - error("Control channel already created with %s on adapter %s", - address, srcstr); - goto drop; - } - - mcl = find_mcl(mi->cached, &dst); - if (!mcl) { - mcl = g_new0(struct mcap_mcl, 1); - mcl->mi = mcap_instance_ref(mi); - bacpy(&mcl->addr, &dst); - set_default_cb(mcl); - mcl->next_mdl = (rand() % MCAP_MDLID_FINAL) + 1; - } - - set_mcl_conf(chan, mcl); - - return; -drop: - g_io_channel_shutdown(chan, TRUE, NULL); -} - -struct mcap_instance *mcap_create_instance(const bdaddr_t *src, - BtIOSecLevel sec, - uint16_t ccpsm, - uint16_t dcpsm, - mcap_mcl_event_cb mcl_connected, - mcap_mcl_event_cb mcl_reconnected, - mcap_mcl_event_cb mcl_disconnected, - mcap_mcl_event_cb mcl_uncached, - mcap_info_ind_event_cb mcl_sync_info_ind, - gpointer user_data, - GError **gerr) -{ - struct mcap_instance *mi; - - 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_connected && mcl_reconnected && - mcl_disconnected && mcl_uncached)) { - g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, - "The callbacks can't be null"); - return NULL; - } - - mi = g_new0(struct mcap_instance, 1); - - bacpy(&mi->src, src); - - mi->sec = sec; - mi->mcl_connected_cb = mcl_connected; - mi->mcl_reconnected_cb = mcl_reconnected; - mi->mcl_disconnected_cb = mcl_disconnected; - mi->mcl_uncached_cb = mcl_uncached; - mi->mcl_sync_infoind_cb = mcl_sync_info_ind; - mi->user_data = user_data; - mi->csp_enabled = FALSE; - - /* Listen incoming connections in control channel */ - mi->ccio = bt_io_listen(connect_mcl_event_cb, NULL, mi, - NULL, gerr, - BT_IO_OPT_SOURCE_BDADDR, &mi->src, - BT_IO_OPT_PSM, ccpsm, - BT_IO_OPT_MTU, MCAP_CC_MTU, - BT_IO_OPT_SEC_LEVEL, sec, - BT_IO_OPT_MODE, L2CAP_MODE_ERTM, - BT_IO_OPT_INVALID); - if (!mi->ccio) { - error("%s", (*gerr)->message); - g_free(mi); - return NULL; - } - - /* Listen incoming connections in data channels */ - mi->dcio = bt_io_listen(connect_dc_event_cb, NULL, mi, - NULL, gerr, - BT_IO_OPT_SOURCE_BDADDR, &mi->src, - BT_IO_OPT_PSM, dcpsm, - BT_IO_OPT_MTU, MCAP_DC_MTU, - BT_IO_OPT_SEC_LEVEL, sec, - BT_IO_OPT_INVALID); - if (!mi->dcio) { - g_io_channel_shutdown(mi->ccio, TRUE, NULL); - g_io_channel_unref(mi->ccio); - mi->ccio = NULL; - error("%s", (*gerr)->message); - g_free(mi); - return NULL; - } - - /* Initialize random seed to generate mdlids for this instance */ - srand(time(NULL)); - - return mcap_instance_ref(mi); -} - -void mcap_release_instance(struct mcap_instance *mi) -{ - GSList *l; - - if (!mi) - return; - - if (mi->ccio) { - g_io_channel_shutdown(mi->ccio, TRUE, NULL); - g_io_channel_unref(mi->ccio); - mi->ccio = NULL; - } - - if (mi->dcio) { - g_io_channel_shutdown(mi->dcio, TRUE, NULL); - g_io_channel_unref(mi->dcio); - mi->dcio = NULL; - } - - for (l = mi->mcls; l; l = l->next) { - mcap_mcl_release(l->data); - mcap_mcl_unref(l->data); - } - - g_slist_free(mi->mcls); - mi->mcls = NULL; - - for (l = mi->cached; l; l = l->next) { - mcap_mcl_release(l->data); - mcap_mcl_unref(l->data); - } - - g_slist_free(mi->cached); - mi->cached = NULL; -} - -struct mcap_instance *mcap_instance_ref(struct mcap_instance *mi) -{ - mi->ref++; - - DBG("mcap_instance_ref(%p): ref=%d", mi, mi->ref); - - return mi; -} - -void mcap_instance_unref(struct mcap_instance *mi) -{ - mi->ref--; - - DBG("mcap_instance_unref(%p): ref=%d", mi, mi->ref); - - if (mi->ref > 0) - return; - - mcap_release_instance(mi); - g_free(mi); -} - -uint16_t mcap_get_ctrl_psm(struct mcap_instance *mi, GError **err) -{ - uint16_t lpsm; - - if (!(mi && mi->ccio)) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, - "Invalid MCAP instance"); - return 0; - } - - if (!bt_io_get(mi->ccio, err, BT_IO_OPT_PSM, &lpsm, BT_IO_OPT_INVALID)) - return 0; - - return lpsm; -} - -uint16_t mcap_get_data_psm(struct mcap_instance *mi, GError **err) -{ - uint16_t lpsm; - - if (!(mi && mi->dcio)) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, - "Invalid MCAP instance"); - return 0; - } - - if (!bt_io_get(mi->dcio, err, BT_IO_OPT_PSM, &lpsm, BT_IO_OPT_INVALID)) - return 0; - - return lpsm; -} - -gboolean mcap_set_data_chan_mode(struct mcap_instance *mi, uint8_t mode, - GError **err) -{ - if (!(mi && mi->dcio)) { - g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, - "Invalid MCAP instance"); - return FALSE; - } - - return bt_io_set(mi->dcio, err, BT_IO_OPT_MODE, mode, - BT_IO_OPT_INVALID); -} - -struct mcap_mdl *mcap_mdl_ref(struct mcap_mdl *mdl) -{ - mdl->ref++; - - DBG("mcap_mdl_ref(%p): ref=%d", mdl, mdl->ref); - - return mdl; -} - -void mcap_mdl_unref(struct mcap_mdl *mdl) -{ - mdl->ref--; - - DBG("mcap_mdl_unref(%p): ref=%d", mdl, mdl->ref); - - if (mdl->ref > 0) - return; - - free_mdl(mdl); -} diff --git a/profiles/health/mcap.h b/profiles/health/mcap.h deleted file mode 100644 index 1129e69..0000000 --- a/profiles/health/mcap.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * - * MCAP for BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. - * Copyright (C) 2010 Signove - * - * 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 65535 - -/* 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 - -/* 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 - -/* 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_UNSPECIFIED_ERROR 0x09 -#define MCAP_REQUEST_NOT_SUPPORTED 0x0A -#define MCAP_CONFIGURATION_REJECTED 0x0B - -/* MDL IDs */ -#define MCAP_MDLID_RESERVED 0x0000 -#define MCAP_MDLID_INITIAL 0x0001 -#define MCAP_MDLID_FINAL 0xFEFF -#define MCAP_ALL_MDLIDS 0xFFFF - -/* MDEP IDs */ -#define MCAP_MDEPID_INITIAL 0x00 -#define MCAP_MDEPID_FINAL 0x7F - -/* CSP special values */ -#define MCAP_BTCLOCK_IMMEDIATE 0xffffffffUL -#define MCAP_TMSTAMP_DONTSET 0xffffffffffffffffULL -#define MCAP_BTCLOCK_MAX 0x0fffffff -#define MCAP_BTCLOCK_FIELD (MCAP_BTCLOCK_MAX + 1) - -/* - * 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; - uint8_t data[0]; -} __attribute__ ((packed)) mcap_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; -} __attribute__ ((packed)) mcap_md_sync_rsp; - -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 { - int8_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/profiles/health/mcap_internal.h b/profiles/health/mcap_internal.h deleted file mode 100644 index 7191b23..0000000 --- a/profiles/health/mcap_internal.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * - * MCAP for BlueZ - Bluetooth protocol stack for Linux - * - * 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_INTERNAL_H -#define __MCAP_INTERNAL_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - MCL_CONNECTED, - MCL_PENDING, - MCL_ACTIVE, - MCL_IDLE -} MCLState; - -typedef enum { - MCL_ACCEPTOR, - MCL_INITIATOR -} MCLRole; - -typedef enum { - MCL_AVAILABLE, - MCL_WAITING_RSP -} MCAPCtrl; - -typedef enum { - MDL_WAITING, - MDL_CONNECTED, - MDL_DELETING, - MDL_CLOSED -} MDLState; - -struct mcap_mdl_cb { - mcap_mdl_event_cb mdl_connected; /* Remote device has created a MDL */ - mcap_mdl_event_cb mdl_closed; /* Remote device has closed a MDL */ - mcap_mdl_event_cb mdl_deleted; /* Remote device requested deleting a MDL */ - mcap_mdl_event_cb mdl_aborted; /* Remote device aborted the mdl creation */ - mcap_remote_mdl_conn_req_cb mdl_conn_req; /* Remote device requested creating a MDL */ - mcap_remote_mdl_reconn_req_cb mdl_reconn_req; /* Remote device requested reconnecting a MDL */ - gpointer user_data; /* User data */ -}; - -struct mcap_instance { - bdaddr_t src; /* Source address */ - GIOChannel *ccio; /* Control Channel IO */ - GIOChannel *dcio; /* Data Channel IO */ - GSList *mcls; /* MCAP instance list */ - GSList *cached; /* List with all cached MCLs (MAX_CACHED macro) */ - BtIOSecLevel sec; /* Security level */ - mcap_mcl_event_cb mcl_connected_cb; /* New MCL connected */ - mcap_mcl_event_cb mcl_reconnected_cb; /* Old MCL has been reconnected */ - mcap_mcl_event_cb mcl_disconnected_cb; /* MCL disconnected */ - mcap_mcl_event_cb mcl_uncached_cb; /* MCL has been removed from MCAP cache */ - mcap_info_ind_event_cb mcl_sync_infoind_cb; /* (CSP Master) Received info indication */ - gpointer user_data; /* Data to be provided in callbacks */ - int ref; /* Reference counter */ - - gboolean csp_enabled; /* CSP: functionality enabled */ -}; - -struct mcap_csp; -struct mcap_mdl_op_cb; - -struct mcap_mcl { - struct mcap_instance *mi; /* MCAP instance 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; /* Current MCL State */ - MCLRole role; /* Initiator or acceptor of this MCL */ - MCAPCtrl req; /* Request control flag */ - struct mcap_mdl_op_cb *priv_data; /* Temporal data to manage responses */ - struct mcap_mdl_cb *cb; /* MDL callbacks */ - guint tid; /* Timer id for waiting for a response */ - uint8_t *lcmd; /* Last command sent */ - int ref; /* References counter */ - uint8_t ctrl; /* MCL control flag */ - uint16_t next_mdl; /* id used to create next MDL */ - struct mcap_csp *csp; /* CSP control structure */ -}; - -#define MCAP_CTRL_CACHED 0x01 /* MCL is cached */ -#define MCAP_CTRL_STD_OP 0x02 /* Support for standard op codes */ -#define MCAP_CTRL_SYNC_OP 0x04 /* Support for synchronization commands */ -#define MCAP_CTRL_CONN 0x08 /* MCL is in connecting process */ -#define MCAP_CTRL_FREE 0x10 /* MCL is marked as releasable */ -#define MCAP_CTRL_NOCACHE 0x20 /* MCL is marked as not cacheable */ - -struct mcap_mdl { - struct mcap_mcl *mcl; /* MCL where this MDL belongs */ - 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 */ - int ref; /* References counter */ -}; - -struct sync_info_ind_data { - uint32_t btclock; - uint64_t timestamp; - uint16_t accuracy; -}; - -int mcap_send_data(int sock, const void *buf, uint32_t size); - -void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len); -void mcap_sync_init(struct mcap_mcl *mcl); -void mcap_sync_stop(struct mcap_mcl *mcl); - -#ifdef __cplusplus -} -#endif - -#endif /* __MCAP_INTERNAL_H */ diff --git a/profiles/health/mcap_lib.h b/profiles/health/mcap_lib.h deleted file mode 100644 index 603ccc0..0000000 --- a/profiles/health/mcap_lib.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * - * MCAP for BlueZ - Bluetooth protocol stack for Linux - * - * 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 - -typedef enum { -/* MCAP Error Response Codes */ - MCAP_ERROR_INVALID_OP_CODE = 1, - MCAP_ERROR_INVALID_PARAM_VALUE, - MCAP_ERROR_INVALID_MDEP, - MCAP_ERROR_MDEP_BUSY, - MCAP_ERROR_INVALID_MDL, - MCAP_ERROR_MDL_BUSY, - MCAP_ERROR_INVALID_OPERATION, - MCAP_ERROR_RESOURCE_UNAVAILABLE, - MCAP_ERROR_UNSPECIFIED_ERROR, - MCAP_ERROR_REQUEST_NOT_SUPPORTED, - MCAP_ERROR_CONFIGURATION_REJECTED, -/* MCAP Internal Errors */ - MCAP_ERROR_INVALID_ARGS, - MCAP_ERROR_ALREADY_EXISTS, - MCAP_ERROR_REQ_IGNORED, - MCAP_ERROR_MCL_CLOSED, - MCAP_ERROR_FAILED -} McapError; - -typedef enum { - MCAP_MDL_CB_INVALID, - MCAP_MDL_CB_CONNECTED, /* mcap_mdl_event_cb */ - MCAP_MDL_CB_CLOSED, /* mcap_mdl_event_cb */ - MCAP_MDL_CB_DELETED, /* mcap_mdl_event_cb */ - MCAP_MDL_CB_ABORTED, /* mcap_mdl_event_cb */ - MCAP_MDL_CB_REMOTE_CONN_REQ, /* mcap_remote_mdl_conn_req_cb */ - MCAP_MDL_CB_REMOTE_RECONN_REQ /* mcap_remote_mdl_reconn_req_cb */ -} McapMclCb; - -struct mcap_instance; -struct mcap_mcl; -struct mcap_mdl; -struct sync_info_ind_data; - -/************ 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_notify_cb) (GError *err, gpointer data); - -/* Next function should return an MCAP appropriate 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); - -/* CSP callbacks */ - -typedef void (* mcap_info_ind_event_cb) (struct mcap_mcl *mcl, - struct sync_info_ind_data *data); - -typedef void (* mcap_sync_cap_cb) (struct mcap_mcl *mcl, - uint8_t mcap_err, - uint8_t btclockres, - uint16_t synclead, - uint16_t tmstampres, - uint16_t tmstampacc, - GError *err, - gpointer data); - -typedef void (* mcap_sync_set_cb) (struct mcap_mcl *mcl, - uint8_t mcap_err, - uint32_t btclock, - uint64_t timestamp, - uint16_t accuracy, - GError *err, - gpointer data); - -/************ Operations ************/ - -/* MDL operations */ - -gboolean mcap_create_mdl(struct mcap_mcl *mcl, - uint8_t mdepid, - uint8_t conf, - mcap_mdl_operation_conf_cb connect_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err); -gboolean mcap_reconnect_mdl(struct mcap_mdl *mdl, - mcap_mdl_operation_cb reconnect_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err); -gboolean mcap_delete_all_mdls(struct mcap_mcl *mcl, - mcap_mdl_notify_cb delete_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err); -gboolean mcap_delete_mdl(struct mcap_mdl *mdl, - mcap_mdl_notify_cb delete_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err); -gboolean mcap_connect_mdl(struct mcap_mdl *mdl, - uint8_t mode, - uint16_t dcpsm, - mcap_mdl_operation_cb connect_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err); -gboolean mcap_mdl_abort(struct mcap_mdl *mdl, - mcap_mdl_notify_cb abort_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err); - -int mcap_mdl_get_fd(struct mcap_mdl *mdl); -uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl); - -struct mcap_mdl *mcap_mdl_ref(struct mcap_mdl *mdl); -void mcap_mdl_unref(struct mcap_mdl *mdl); - -/* MCL operations */ - -gboolean mcap_create_mcl(struct mcap_instance *mi, - const bdaddr_t *addr, - uint16_t ccpsm, - mcap_mcl_connect_cb connect_cb, - gpointer user_data, - GDestroyNotify destroy, - GError **err); -void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache); -gboolean mcap_mcl_set_cb(struct mcap_mcl *mcl, gpointer user_data, - GError **gerr, McapMclCb cb1, ...); -void mcap_mcl_get_addr(struct mcap_mcl *mcl, bdaddr_t *addr); - -struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl); -void mcap_mcl_unref(struct mcap_mcl *mcl); - -/* CSP operations */ - -void mcap_enable_csp(struct mcap_instance *mi); -void mcap_disable_csp(struct mcap_instance *mi); - -uint64_t mcap_get_timestamp(struct mcap_mcl *mcl, - struct timespec *given_time); -uint32_t mcap_get_btclock(struct mcap_mcl *mcl); - -void mcap_sync_cap_req(struct mcap_mcl *mcl, - uint16_t reqacc, - mcap_sync_cap_cb cb, - gpointer user_data, - GError **err); - -void mcap_sync_set_req(struct mcap_mcl *mcl, - uint8_t update, - uint32_t btclock, - uint64_t timestamp, - mcap_sync_set_cb cb, - gpointer user_data, - GError **err); - -/* MCAP main operations */ - -struct mcap_instance *mcap_create_instance(const bdaddr_t *src, - BtIOSecLevel sec, uint16_t ccpsm, - uint16_t dcpsm, - mcap_mcl_event_cb mcl_connected, - mcap_mcl_event_cb mcl_reconnected, - mcap_mcl_event_cb mcl_disconnected, - mcap_mcl_event_cb mcl_uncached, - mcap_info_ind_event_cb mcl_sync_info_ind, - gpointer user_data, - GError **gerr); -void mcap_release_instance(struct mcap_instance *mi); - -struct mcap_instance *mcap_instance_ref(struct mcap_instance *mi); -void mcap_instance_unref(struct mcap_instance *mi); - -uint16_t mcap_get_ctrl_psm(struct mcap_instance *mi, GError **err); -uint16_t mcap_get_data_psm(struct mcap_instance *mi, GError **err); - -gboolean mcap_set_data_chan_mode(struct mcap_instance *mi, uint8_t mode, - GError **err); - -#ifdef __cplusplus -} -#endif - -#endif /* __MCAP_LIB_H */ diff --git a/profiles/health/mcap_sync.c b/profiles/health/mcap_sync.c deleted file mode 100644 index a0cc02a..0000000 --- a/profiles/health/mcap_sync.c +++ /dev/null @@ -1,1009 +0,0 @@ -/* - * - * MCAP for BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. - * Copyright (C) 2010 Signove - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdint.h> -#include <netinet/in.h> -#include <time.h> -#include <stdlib.h> -#include <sys/ioctl.h> - -#include <bluetooth/bluetooth.h> -#include <bluetooth/l2cap.h> - -#include "btio/btio.h" -#include "src/adapter.h" -#include "src/log.h" - -#include "mcap.h" -#include "mcap_lib.h" -#include "mcap_internal.h" - -#define MCAP_BTCLOCK_HALF (MCAP_BTCLOCK_FIELD / 2) -#define CLK CLOCK_MONOTONIC - -#define MCAP_CSP_ERROR g_quark_from_static_string("mcap-csp-error-quark") -#define MAX_RETRIES 10 -#define SAMPLE_COUNT 20 - -struct mcap_csp { - uint64_t base_tmstamp; /* CSP base timestamp */ - struct timespec base_time; /* CSP base time when timestamp set */ - guint local_caps; /* CSP-Master: have got remote caps */ - guint remote_caps; /* CSP-Slave: remote master got caps */ - guint rem_req_acc; /* CSP-Slave: accuracy required by master */ - guint ind_expected; /* CSP-Master: indication expected */ - uint8_t csp_req; /* CSP-Master: Request control flag */ - guint ind_timer; /* CSP-Slave: indication timer */ - guint set_timer; /* CSP-Slave: delayed set timer */ - void *set_data; /* CSP-Slave: delayed set data */ - void *csp_priv_data; /* CSP-Master: In-flight request data */ -}; - -struct mcap_sync_cap_cbdata { - mcap_sync_cap_cb cb; - gpointer user_data; -}; - -struct mcap_sync_set_cbdata { - mcap_sync_set_cb cb; - gpointer user_data; -}; - -struct csp_caps { - int ts_acc; /* timestamp accuracy */ - int ts_res; /* timestamp resolution */ - int latency; /* Read BT clock latency */ - int preempt_thresh; /* Preemption threshold for latency */ - int syncleadtime_ms; /* SyncLeadTime in ms */ -}; - -struct sync_set_data { - uint8_t update; - uint32_t sched_btclock; - uint64_t timestamp; - int ind_freq; - gboolean role; -}; - -static gboolean csp_caps_initialized = FALSE; -struct csp_caps _caps; - -static int send_sync_cmd(struct mcap_mcl *mcl, const void *buf, uint32_t size) -{ - int sock; - - if (mcl->cc == NULL) - return -1; - - sock = g_io_channel_unix_get_fd(mcl->cc); - return mcap_send_data(sock, buf, size); -} - -static int send_unsupported_cap_req(struct mcap_mcl *mcl) -{ - mcap_md_sync_cap_rsp *cmd; - int sent; - - cmd = g_new0(mcap_md_sync_cap_rsp, 1); - cmd->op = MCAP_MD_SYNC_CAP_RSP; - cmd->rc = MCAP_REQUEST_NOT_SUPPORTED; - - sent = send_sync_cmd(mcl, cmd, sizeof(*cmd)); - g_free(cmd); - - return sent; -} - -static int send_unsupported_set_req(struct mcap_mcl *mcl) -{ - mcap_md_sync_set_rsp *cmd; - int sent; - - cmd = g_new0(mcap_md_sync_set_rsp, 1); - cmd->op = MCAP_MD_SYNC_SET_RSP; - cmd->rc = MCAP_REQUEST_NOT_SUPPORTED; - - sent = send_sync_cmd(mcl, cmd, sizeof(*cmd)); - g_free(cmd); - - return sent; -} - -static void reset_tmstamp(struct mcap_csp *csp, struct timespec *base_time, - uint64_t new_tmstamp) -{ - csp->base_tmstamp = new_tmstamp; - if (base_time) - csp->base_time = *base_time; - else - clock_gettime(CLK, &csp->base_time); -} - -void mcap_sync_init(struct mcap_mcl *mcl) -{ - if (!mcl->mi->csp_enabled) { - mcl->csp = NULL; - return; - } - - mcl->csp = g_new0(struct mcap_csp, 1); - - mcl->csp->rem_req_acc = 10000; /* safe divisor */ - mcl->csp->set_data = NULL; - mcl->csp->csp_priv_data = NULL; - - reset_tmstamp(mcl->csp, NULL, 0); -} - -void mcap_sync_stop(struct mcap_mcl *mcl) -{ - if (!mcl->csp) - return; - - if (mcl->csp->ind_timer) - g_source_remove(mcl->csp->ind_timer); - - if (mcl->csp->set_timer) - g_source_remove(mcl->csp->set_timer); - - if (mcl->csp->set_data) - g_free(mcl->csp->set_data); - - if (mcl->csp->csp_priv_data) - g_free(mcl->csp->csp_priv_data); - - mcl->csp->ind_timer = 0; - mcl->csp->set_timer = 0; - mcl->csp->set_data = NULL; - mcl->csp->csp_priv_data = NULL; - - g_free(mcl->csp); - mcl->csp = NULL; -} - -static uint64_t time_us(struct timespec *tv) -{ - return tv->tv_sec * 1000000ll + tv->tv_nsec / 1000ll; -} - -static int64_t bt2us(int bt) -{ - return bt * 312.5; -} - -static int bt2ms(int bt) -{ - return bt * 312.5 / 1000; -} - -static int btoffset(uint32_t btclk1, uint32_t btclk2) -{ - int offset = btclk2 - btclk1; - - if (offset <= -MCAP_BTCLOCK_HALF) - offset += MCAP_BTCLOCK_FIELD; - else if (offset > MCAP_BTCLOCK_HALF) - offset -= MCAP_BTCLOCK_FIELD; - - return offset; -} - -static int btdiff(uint32_t btclk1, uint32_t btclk2) -{ - return btoffset(btclk1, btclk2); -} - -static gboolean valid_btclock(uint32_t btclk) -{ - return btclk <= MCAP_BTCLOCK_MAX; -} - -/* This call may fail; either deal with retry or use read_btclock_retry */ -static gboolean read_btclock(struct mcap_mcl *mcl, uint32_t *btclock, - uint16_t *btaccuracy) -{ - int which = 1; - struct btd_adapter *adapter; - - adapter = adapter_find(&mcl->mi->src); - if (!adapter) - return FALSE; - - if (btd_adapter_read_clock(adapter, &mcl->addr, which, 1000, - btclock, btaccuracy) < 0) - return FALSE; - - return TRUE; -} - -static gboolean read_btclock_retry(struct mcap_mcl *mcl, uint32_t *btclock, - uint16_t *btaccuracy) -{ - int retries = 5; - - while (--retries >= 0) { - if (read_btclock(mcl, btclock, btaccuracy)) - return TRUE; - DBG("CSP: retrying to read bt clock..."); - } - - return FALSE; -} - -static gboolean get_btrole(struct mcap_mcl *mcl) -{ - int sock, flags; - socklen_t len; - - if (mcl->cc == NULL) - return -1; - - sock = g_io_channel_unix_get_fd(mcl->cc); - len = sizeof(flags); - - if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len)) - DBG("CSP: could not read role"); - - return flags & L2CAP_LM_MASTER; -} - -uint64_t mcap_get_timestamp(struct mcap_mcl *mcl, - struct timespec *given_time) -{ - struct timespec now; - uint64_t tmstamp; - - if (!mcl->csp) - return MCAP_TMSTAMP_DONTSET; - - if (given_time) - now = *given_time; - else - clock_gettime(CLK, &now); - - tmstamp = time_us(&now) - time_us(&mcl->csp->base_time) - + mcl->csp->base_tmstamp; - - return tmstamp; -} - -uint32_t mcap_get_btclock(struct mcap_mcl *mcl) -{ - uint32_t btclock; - uint16_t accuracy; - - if (!mcl->csp) - return MCAP_BTCLOCK_IMMEDIATE; - - if (!read_btclock_retry(mcl, &btclock, &accuracy)) - btclock = 0xffffffff; - - return btclock; -} - -static gboolean initialize_caps(struct mcap_mcl *mcl) -{ - struct timespec t1, t2; - int latencies[SAMPLE_COUNT]; - int latency, avg, dev; - uint32_t btclock; - uint16_t btaccuracy; - int i; - int retries; - - clock_getres(CLK, &t1); - - _caps.ts_res = time_us(&t1); - if (_caps.ts_res < 1) - _caps.ts_res = 1; - - _caps.ts_acc = 20; /* ppm, estimated */ - - /* A little exercise before measuing latency */ - clock_gettime(CLK, &t1); - read_btclock_retry(mcl, &btclock, &btaccuracy); - - /* Read clock a number of times and measure latency */ - avg = 0; - i = 0; - retries = MAX_RETRIES; - while (i < SAMPLE_COUNT && retries > 0) { - clock_gettime(CLK, &t1); - if (!read_btclock(mcl, &btclock, &btaccuracy)) { - retries--; - continue; - } - clock_gettime(CLK, &t2); - - latency = time_us(&t2) - time_us(&t1); - latencies[i] = latency; - avg += latency; - i++; - } - - if (retries <= 0) - return FALSE; - - /* Calculate average and deviation */ - avg /= SAMPLE_COUNT; - dev = 0; - for (i = 0; i < SAMPLE_COUNT; ++i) - dev += abs(latencies[i] - avg); - dev /= SAMPLE_COUNT; - - /* Calculate corrected average, without 'freak' latencies */ - latency = 0; - for (i = 0; i < SAMPLE_COUNT; ++i) { - if (latencies[i] > (avg + dev * 6)) - latency += avg; - else - latency += latencies[i]; - } - latency /= SAMPLE_COUNT; - - _caps.latency = latency; - _caps.preempt_thresh = latency * 4; - _caps.syncleadtime_ms = latency * 50 / 1000; - - csp_caps_initialized = TRUE; - return TRUE; -} - -static struct csp_caps *caps(struct mcap_mcl *mcl) -{ - if (!csp_caps_initialized) - if (!initialize_caps(mcl)) { - /* Temporary failure in reading BT clock */ - return NULL; - } - - return &_caps; -} - -static int send_sync_cap_rsp(struct mcap_mcl *mcl, uint8_t rspcode, - uint8_t btclockres, uint16_t synclead, - uint16_t tmstampres, uint16_t tmstampacc) -{ - mcap_md_sync_cap_rsp *rsp; - int sent; - - rsp = g_new0(mcap_md_sync_cap_rsp, 1); - - rsp->op = MCAP_MD_SYNC_CAP_RSP; - rsp->rc = rspcode; - - rsp->btclock = btclockres; - rsp->sltime = htons(synclead); - rsp->timestnr = htons(tmstampres); - rsp->timestna = htons(tmstampacc); - - sent = send_sync_cmd(mcl, rsp, sizeof(*rsp)); - g_free(rsp); - - return sent; -} - -static void proc_sync_cap_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) -{ - mcap_md_sync_cap_req *req; - uint16_t required_accuracy; - uint16_t our_accuracy; - uint32_t btclock; - uint16_t btres; - - if (len != sizeof(mcap_md_sync_cap_req)) { - send_sync_cap_rsp(mcl, MCAP_INVALID_PARAM_VALUE, - 0, 0, 0, 0); - return; - } - - if (!caps(mcl)) { - send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE, - 0, 0, 0, 0); - return; - } - - req = (mcap_md_sync_cap_req *) cmd; - required_accuracy = ntohs(req->timest); - our_accuracy = caps(mcl)->ts_acc; - - if (required_accuracy < our_accuracy || required_accuracy < 1) { - send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE, - 0, 0, 0, 0); - return; - } - - if (!read_btclock_retry(mcl, &btclock, &btres)) { - send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE, - 0, 0, 0, 0); - return; - } - - mcl->csp->remote_caps = 1; - mcl->csp->rem_req_acc = required_accuracy; - - send_sync_cap_rsp(mcl, MCAP_SUCCESS, btres, - caps(mcl)->syncleadtime_ms, - caps(mcl)->ts_res, our_accuracy); -} - -static int send_sync_set_rsp(struct mcap_mcl *mcl, uint8_t rspcode, - uint32_t btclock, uint64_t timestamp, - uint16_t tmstampres) -{ - mcap_md_sync_set_rsp *rsp; - int sent; - - rsp = g_new0(mcap_md_sync_set_rsp, 1); - - rsp->op = MCAP_MD_SYNC_SET_RSP; - rsp->rc = rspcode; - rsp->btclock = htonl(btclock); - rsp->timestst = hton64(timestamp); - rsp->timestsa = htons(tmstampres); - - sent = send_sync_cmd(mcl, rsp, sizeof(*rsp)); - g_free(rsp); - - return sent; -} - -static gboolean get_all_clocks(struct mcap_mcl *mcl, uint32_t *btclock, - struct timespec *base_time, - uint64_t *timestamp) -{ - int latency; - int retry = 5; - uint16_t btres; - struct timespec t0; - - if (!caps(mcl)) - return FALSE; - - latency = caps(mcl)->preempt_thresh + 1; - - while (latency > caps(mcl)->preempt_thresh && --retry >= 0) { - - clock_gettime(CLK, &t0); - - if (!read_btclock(mcl, btclock, &btres)) - continue; - - clock_gettime(CLK, base_time); - - /* Tries to detect preemption between clock_gettime - * and read_btclock by measuring transaction time - */ - latency = time_us(base_time) - time_us(&t0); - } - - *timestamp = mcap_get_timestamp(mcl, base_time); - - return TRUE; -} - -static gboolean sync_send_indication(gpointer user_data) -{ - struct mcap_mcl *mcl; - mcap_md_sync_info_ind *cmd; - uint32_t btclock; - uint64_t tmstamp; - struct timespec base_time; - int sent; - - if (!user_data) - return FALSE; - - mcl = user_data; - - if (!caps(mcl)) - return FALSE; - - if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp)) - return FALSE; - - cmd = g_new0(mcap_md_sync_info_ind, 1); - - cmd->op = MCAP_MD_SYNC_INFO_IND; - cmd->btclock = htonl(btclock); - cmd->timestst = hton64(tmstamp); - cmd->timestsa = htons(caps(mcl)->latency); - - sent = send_sync_cmd(mcl, cmd, sizeof(*cmd)); - g_free(cmd); - - return !sent; -} - -static gboolean proc_sync_set_req_phase2(gpointer user_data) -{ - struct mcap_mcl *mcl; - struct sync_set_data *data; - uint8_t update; - uint32_t sched_btclock; - uint64_t new_tmstamp; - int ind_freq; - int role; - uint32_t btclock; - uint64_t tmstamp; - struct timespec base_time; - uint16_t tmstampacc; - gboolean reset; - int delay; - - if (!user_data) - return FALSE; - - mcl = user_data; - - if (!mcl->csp->set_data) - return FALSE; - - data = mcl->csp->set_data; - update = data->update; - sched_btclock = data->sched_btclock; - new_tmstamp = data->timestamp; - ind_freq = data->ind_freq; - role = data->role; - - if (!caps(mcl)) { - send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0); - return FALSE; - } - - if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp)) { - send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0); - return FALSE; - } - - if (get_btrole(mcl) != role) { - send_sync_set_rsp(mcl, MCAP_INVALID_OPERATION, 0, 0, 0); - return FALSE; - } - - reset = (new_tmstamp != MCAP_TMSTAMP_DONTSET); - - if (reset) { - if (sched_btclock != MCAP_BTCLOCK_IMMEDIATE) { - delay = bt2us(btdiff(sched_btclock, btclock)); - if (delay >= 0 || ((new_tmstamp - delay) > 0)) { - new_tmstamp += delay; - DBG("CSP: reset w/ delay %dus, compensated", - delay); - } else - DBG("CSP: reset w/ delay %dus, uncompensated", - delay); - } - - reset_tmstamp(mcl->csp, &base_time, new_tmstamp); - tmstamp = new_tmstamp; - } - - tmstampacc = caps(mcl)->latency + caps(mcl)->ts_acc; - - if (mcl->csp->ind_timer) { - g_source_remove(mcl->csp->ind_timer); - mcl->csp->ind_timer = 0; - } - - if (update) { - int when = ind_freq + caps(mcl)->syncleadtime_ms; - mcl->csp->ind_timer = g_timeout_add(when, - sync_send_indication, - mcl); - } - - send_sync_set_rsp(mcl, MCAP_SUCCESS, btclock, tmstamp, tmstampacc); - - /* First indication after set is immediate */ - if (update) - sync_send_indication(mcl); - - return FALSE; -} - -static void proc_sync_set_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) -{ - mcap_md_sync_set_req *req; - uint32_t sched_btclock, cur_btclock; - uint16_t btres; - uint8_t update; - uint64_t timestamp; - struct sync_set_data *set_data; - int phase2_delay, ind_freq, when; - - if (len != sizeof(mcap_md_sync_set_req)) { - send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); - return; - } - - req = (mcap_md_sync_set_req *) cmd; - sched_btclock = ntohl(req->btclock); - update = req->timestui; - timestamp = ntoh64(req->timestst); - - if (sched_btclock != MCAP_BTCLOCK_IMMEDIATE && - !valid_btclock(sched_btclock)) { - send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); - return; - } - - if (update > 1) { - send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); - return; - } - - if (!mcl->csp->remote_caps) { - /* Remote side did not ask our capabilities yet */ - send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); - return; - } - - if (!caps(mcl)) { - send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0); - return; - } - - if (!read_btclock_retry(mcl, &cur_btclock, &btres)) { - send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0); - return; - } - - if (sched_btclock == MCAP_BTCLOCK_IMMEDIATE) - phase2_delay = 0; - else { - phase2_delay = btdiff(cur_btclock, sched_btclock); - - if (phase2_delay < 0) { - /* can not reset in the past tense */ - send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, - 0, 0, 0); - return; - } - - /* Convert to miliseconds */ - phase2_delay = bt2ms(phase2_delay); - - if (phase2_delay > 61*1000) { - /* More than 60 seconds in the future */ - send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, - 0, 0, 0); - return; - } else if (phase2_delay < caps(mcl)->latency / 1000) { - /* Too fast for us to do in time */ - send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, - 0, 0, 0); - return; - } - } - - if (update) { - /* Indication frequency: required accuracy divided by ours */ - /* Converted to milisseconds */ - ind_freq = (1000 * mcl->csp->rem_req_acc) / caps(mcl)->ts_acc; - - if (ind_freq < MAX(caps(mcl)->latency * 2 / 1000, 100)) { - /* Too frequent, we can't handle */ - send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, - 0, 0, 0); - return; - } - - DBG("CSP: indication every %dms", ind_freq); - } else - ind_freq = 0; - - if (mcl->csp->ind_timer) { - /* Old indications are no longer sent */ - g_source_remove(mcl->csp->ind_timer); - mcl->csp->ind_timer = 0; - } - - if (!mcl->csp->set_data) - mcl->csp->set_data = g_new0(struct sync_set_data, 1); - - set_data = (struct sync_set_data *) mcl->csp->set_data; - - set_data->update = update; - set_data->sched_btclock = sched_btclock; - set_data->timestamp = timestamp; - set_data->ind_freq = ind_freq; - set_data->role = get_btrole(mcl); - - /* TODO is there some way to schedule a call based directly on - * a BT clock value, instead of this estimation that uses - * the SO clock? */ - - if (phase2_delay > 0) { - when = phase2_delay + caps(mcl)->syncleadtime_ms; - mcl->csp->set_timer = g_timeout_add(when, - proc_sync_set_req_phase2, - mcl); - } else - proc_sync_set_req_phase2(mcl); - - /* First indication is immediate */ - if (update) - sync_send_indication(mcl); -} - -static void proc_sync_cap_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) -{ - mcap_md_sync_cap_rsp *rsp; - uint8_t mcap_err; - uint8_t btclockres; - uint16_t synclead; - uint16_t tmstampres; - uint16_t tmstampacc; - struct mcap_sync_cap_cbdata *cbdata; - mcap_sync_cap_cb cb; - gpointer user_data; - - if (mcl->csp->csp_req != MCAP_MD_SYNC_CAP_REQ) { - DBG("CSP: got unexpected cap respose"); - return; - } - - if (!mcl->csp->csp_priv_data) { - DBG("CSP: no priv data for cap respose"); - return; - } - - cbdata = mcl->csp->csp_priv_data; - cb = cbdata->cb; - user_data = cbdata->user_data; - g_free(cbdata); - - mcl->csp->csp_priv_data = NULL; - mcl->csp->csp_req = 0; - - if (len != sizeof(mcap_md_sync_cap_rsp)) { - DBG("CSP: got corrupted cap respose"); - return; - } - - rsp = (mcap_md_sync_cap_rsp *) cmd; - mcap_err = rsp->rc; - btclockres = rsp->btclock; - synclead = ntohs(rsp->sltime); - tmstampres = ntohs(rsp->timestnr); - tmstampacc = ntohs(rsp->timestna); - - if (!mcap_err) - mcl->csp->local_caps = TRUE; - - cb(mcl, mcap_err, btclockres, synclead, tmstampres, tmstampacc, NULL, - user_data); -} - -static void proc_sync_set_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) -{ - mcap_md_sync_set_rsp *rsp; - uint8_t mcap_err; - uint32_t btclock; - uint64_t timestamp; - uint16_t accuracy; - struct mcap_sync_set_cbdata *cbdata; - mcap_sync_set_cb cb; - gpointer user_data; - - if (mcl->csp->csp_req != MCAP_MD_SYNC_SET_REQ) { - DBG("CSP: got unexpected set respose"); - return; - } - - if (!mcl->csp->csp_priv_data) { - DBG("CSP: no priv data for set respose"); - return; - } - - cbdata = mcl->csp->csp_priv_data; - cb = cbdata->cb; - user_data = cbdata->user_data; - g_free(cbdata); - - mcl->csp->csp_priv_data = NULL; - mcl->csp->csp_req = 0; - - if (len != sizeof(mcap_md_sync_set_rsp)) { - DBG("CSP: got corrupted set respose"); - return; - } - - rsp = (mcap_md_sync_set_rsp *) cmd; - mcap_err = rsp->rc; - btclock = ntohl(rsp->btclock); - timestamp = ntoh64(rsp->timestst); - accuracy = ntohs(rsp->timestsa); - - if (!mcap_err && !valid_btclock(btclock)) - mcap_err = MCAP_ERROR_INVALID_ARGS; - - cb(mcl, mcap_err, btclock, timestamp, accuracy, NULL, user_data); -} - -static void proc_sync_info_ind(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) -{ - mcap_md_sync_info_ind *req; - struct sync_info_ind_data data; - uint32_t btclock; - - if (!mcl->csp->ind_expected) { - DBG("CSP: received unexpected info indication"); - return; - } - - if (len != sizeof(mcap_md_sync_info_ind)) - return; - - req = (mcap_md_sync_info_ind *) cmd; - - btclock = ntohl(req->btclock); - - if (!valid_btclock(btclock)) - return; - - data.btclock = btclock; - data.timestamp = ntoh64(req->timestst); - data.accuracy = ntohs(req->timestsa); - - if (mcl->mi->mcl_sync_infoind_cb) - mcl->mi->mcl_sync_infoind_cb(mcl, &data); -} - -void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) -{ - if (!mcl->mi->csp_enabled || !mcl->csp) { - switch (cmd[0]) { - case MCAP_MD_SYNC_CAP_REQ: - send_unsupported_cap_req(mcl); - break; - case MCAP_MD_SYNC_SET_REQ: - send_unsupported_set_req(mcl); - break; - } - return; - } - - switch (cmd[0]) { - case MCAP_MD_SYNC_CAP_REQ: - proc_sync_cap_req(mcl, cmd, len); - break; - case MCAP_MD_SYNC_CAP_RSP: - proc_sync_cap_rsp(mcl, cmd, len); - break; - case MCAP_MD_SYNC_SET_REQ: - proc_sync_set_req(mcl, cmd, len); - break; - case MCAP_MD_SYNC_SET_RSP: - proc_sync_set_rsp(mcl, cmd, len); - break; - case MCAP_MD_SYNC_INFO_IND: - proc_sync_info_ind(mcl, cmd, len); - break; - } -} - -void mcap_sync_cap_req(struct mcap_mcl *mcl, uint16_t reqacc, - mcap_sync_cap_cb cb, gpointer user_data, - GError **err) -{ - struct mcap_sync_cap_cbdata *cbdata; - mcap_md_sync_cap_req *cmd; - - if (!mcl->mi->csp_enabled || !mcl->csp) { - g_set_error(err, - MCAP_CSP_ERROR, - MCAP_ERROR_RESOURCE_UNAVAILABLE, - "CSP not enabled for the instance"); - return; - } - - if (mcl->csp->csp_req) { - g_set_error(err, - MCAP_CSP_ERROR, - MCAP_ERROR_RESOURCE_UNAVAILABLE, - "Pending CSP request"); - return; - } - - mcl->csp->csp_req = MCAP_MD_SYNC_CAP_REQ; - cmd = g_new0(mcap_md_sync_cap_req, 1); - - cmd->op = MCAP_MD_SYNC_CAP_REQ; - cmd->timest = htons(reqacc); - - cbdata = g_new0(struct mcap_sync_cap_cbdata, 1); - cbdata->cb = cb; - cbdata->user_data = user_data; - mcl->csp->csp_priv_data = cbdata; - - send_sync_cmd(mcl, cmd, sizeof(*cmd)); - - g_free(cmd); -} - -void mcap_sync_set_req(struct mcap_mcl *mcl, uint8_t update, uint32_t btclock, - uint64_t timestamp, mcap_sync_set_cb cb, - gpointer user_data, GError **err) -{ - mcap_md_sync_set_req *cmd; - struct mcap_sync_set_cbdata *cbdata; - - if (!mcl->mi->csp_enabled || !mcl->csp) { - g_set_error(err, - MCAP_CSP_ERROR, - MCAP_ERROR_RESOURCE_UNAVAILABLE, - "CSP not enabled for the instance"); - return; - } - - if (!mcl->csp->local_caps) { - g_set_error(err, - MCAP_CSP_ERROR, - MCAP_ERROR_RESOURCE_UNAVAILABLE, - "Did not get CSP caps from slave yet"); - return; - } - - if (mcl->csp->csp_req) { - g_set_error(err, - MCAP_CSP_ERROR, - MCAP_ERROR_RESOURCE_UNAVAILABLE, - "Pending CSP request"); - return; - } - - mcl->csp->csp_req = MCAP_MD_SYNC_SET_REQ; - cmd = g_new0(mcap_md_sync_set_req, 1); - - cmd->op = MCAP_MD_SYNC_SET_REQ; - cmd->timestui = update; - cmd->btclock = htonl(btclock); - cmd->timestst = hton64(timestamp); - - mcl->csp->ind_expected = update; - - cbdata = g_new0(struct mcap_sync_set_cbdata, 1); - cbdata->cb = cb; - cbdata->user_data = user_data; - mcl->csp->csp_priv_data = cbdata; - - send_sync_cmd(mcl, cmd, sizeof(*cmd)); - - g_free(cmd); -} - -void mcap_enable_csp(struct mcap_instance *mi) -{ - mi->csp_enabled = TRUE; -} - -void mcap_disable_csp(struct mcap_instance *mi) -{ - mi->csp_enabled = FALSE; -} -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html