diff --git a/health/hdp.c b/health/hdp.c index cdaae653c..ccfbfe0b2 100644 --- a/health/hdp.c +++ b/health/hdp.c @@ -105,6 +105,7 @@ static void free_health_channel(struct hdp_channel *chan) if (chan->mdep == HDP_MDEP_ECHO) free_echo_data(chan->edata); + mcap_mdl_unref(chan->mdl); hdp_application_unref(chan->app); health_device_unref(chan->dev); g_free(chan->path); @@ -763,7 +764,7 @@ static struct hdp_channel *create_channel(struct hdp_device *dev, hdp_chann = g_new0(struct hdp_channel, 1); hdp_chann->config = config; hdp_chann->dev = health_device_ref(dev); - hdp_chann->mdl = mdl; + hdp_chann->mdl = mcap_mdl_ref(mdl); hdp_chann->mdlid = mdlid; hdp_chann->app = hdp_application_ref(app); @@ -920,7 +921,7 @@ static void hdp_mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data) return; chan = dev->ndc; - chan->mdl = mdl; + chan->mdl = mcap_mdl_ref(mdl); if (!g_slist_find(dev->channels, chan)) dev->channels = g_slist_prepend(dev->channels, @@ -1004,7 +1005,7 @@ static void hdp_mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data) if (!dev->ndc) return; - dev->ndc->mdl = mdl; + dev->ndc->mdl = mcap_mdl_ref(mdl); if (!g_slist_find(dev->channels, dev->ndc)) dev->channels = g_slist_prepend(dev->channels, @@ -1735,9 +1736,11 @@ fail: /* Send abort request because remote side is now in PENDING */ /* state. Then we have to delete it because we couldn't */ /* register the HealthChannel interface */ - if (!mcap_mdl_abort(mdl, abort_and_del_mdl_cb, mdl, NULL, &gerr)) { + if (!mcap_mdl_abort(mdl, abort_and_del_mdl_cb, mcap_mdl_ref(mdl), NULL, + &gerr)) { error("%s", gerr->message); g_error_free(gerr); + mcap_mdl_unref(mdl); } } diff --git a/health/mcap.c b/health/mcap.c index 8fbc6beba..ab086a78d 100644 --- a/health/mcap.c +++ b/health/mcap.c @@ -263,14 +263,20 @@ static gint cmp_mdl_state(gconstpointer a, gconstpointer b) return 1; } -static void free_mcl_priv_data(struct mcap_mcl *mcl) +static void free_mcap_mdl_op(struct mcap_mdl_op_cb *op) { - struct mcap_mdl_op_cb *op = mcl->priv_data; - if (op->destroy) op->destroy(op->user_data); - g_free(mcl->priv_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; } @@ -290,7 +296,7 @@ static void mcap_notify_error(struct mcap_mcl *mcl, GError *err) l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state); mdl = l->data; mcl->mdls = g_slist_remove(mcl->mdls, mdl); - free_mdl(mdl); + mcap_mdl_unref(mdl); update_mcl_state(mcl); con->cb.op_conf(NULL, 0, err, con->user_data); break; @@ -484,7 +490,7 @@ gboolean mcap_create_mdl(struct mcap_mcl *mcl, mdl->state = MDL_WAITING; con = g_new0(struct mcap_mdl_op_cb, 1); - con->mdl = mdl; + con->mdl = mcap_mdl_ref(mdl); con->cb.op_conf = connect_cb; con->destroy = destroy; con->user_data = user_data; @@ -492,7 +498,7 @@ gboolean mcap_create_mdl(struct mcap_mcl *mcl, cmd = create_mdl_req(id, mdepid, conf); if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_create_mdl_req), err)) { - free_mdl(mdl); + mcap_mdl_unref(con->mdl); g_free(con); g_free(cmd); return FALSE; @@ -501,7 +507,8 @@ gboolean mcap_create_mdl(struct mcap_mcl *mcl, mcl->state = MCL_ACTIVE; mcl->priv_data = con; - mcl->mdls = g_slist_insert_sorted(mcl->mdls, mdl, compare_mdl); + 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; @@ -533,7 +540,7 @@ gboolean mcap_reconnect_mdl(struct mcap_mdl *mdl, mdl->state = MDL_WAITING; - con->mdl = mdl; + con->mdl = mcap_mdl_ref(mdl); con->cb.op = reconnect_cb; con->destroy = destroy; con->user_data = user_data; @@ -630,12 +637,13 @@ gboolean mcap_delete_mdl(struct mcap_mdl *mdl, mcap_mdl_notify_cb delete_cb, mdl->state = MDL_DELETING; con = g_new0(struct mcap_mdl_op_cb, 1); - con->mdl = mdl; + 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; } @@ -666,7 +674,7 @@ gboolean mcap_mdl_abort(struct mcap_mdl *mdl, mcap_mdl_notify_cb abort_cb, return FALSE; } - con->mdl = mdl; + con->mdl = mcap_mdl_ref(mdl); con->cb.notify = abort_cb; con->destroy = destroy; con->user_data = user_data; @@ -742,7 +750,7 @@ static void close_mcl(struct mcap_mcl *mcl, gboolean cache_requested) if (save) return; - g_slist_foreach(mcl->mdls, (GFunc) free_mdl, NULL); + g_slist_foreach(mcl->mdls, (GFunc) mcap_mdl_unref, NULL); g_slist_free(mcl->mdls); mcl->mdls = NULL; } @@ -952,7 +960,7 @@ static void mcap_del_mdl(gpointer elem, gpointer user_data) if (notify) mdl->mcl->cb->mdl_deleted(mdl, mdl->mcl->cb->user_data); - free_mdl(mdl); + mcap_mdl_unref(mdl); } static gboolean check_cmd_req_length(struct mcap_mcl *mcl, void *cmd, @@ -1041,7 +1049,8 @@ static void process_md_create_mdl_req(struct mcap_mcl *mcl, void *cmd, 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, mdl, compare_mdl); + 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 */ @@ -1351,7 +1360,7 @@ static gboolean process_md_create_mdl_rsp(struct mcap_mcl *mcl, fail: connect_cb(NULL, 0, gerr, user_data); mcl->mdls = g_slist_remove(mcl->mdls, mdl); - free_mdl(mdl); + mcap_mdl_unref(mdl); g_error_free(gerr); update_mcl_state(mcl); return close; @@ -1387,7 +1396,7 @@ static gboolean process_md_reconnect_mdl_rsp(struct mcap_mcl *mcl, /* Remove cached mdlid */ mcl->mdls = g_slist_remove(mcl->mdls, mdl); mcl->cb->mdl_deleted(mdl, mcl->cb->user_data); - free_mdl(mdl); + mcap_mdl_unref(mdl); return close; } @@ -1414,7 +1423,7 @@ static gboolean process_md_abort_mdl_rsp(struct mcap_mcl *mcl, 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); - free_mdl(mdl); + mcap_mdl_unref(mdl); } if (gerr) @@ -1447,7 +1456,7 @@ static void check_mdl_del_err(struct mcap_mdl *mdl, mcap_rsp *rsp) /* MDL does not exist in remote side, we can delete it */ mdl->mcl->mdls = g_slist_remove(mdl->mcl->mdls, mdl); - free_mdl(mdl); + mcap_mdl_unref(mdl); } static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp, @@ -1497,18 +1506,14 @@ static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp, static void post_process_rsp(struct mcap_mcl *mcl, struct mcap_mdl_op_cb *op) { - if (op->destroy) - op->destroy(op->user_data); - if (mcl->priv_data != op) { /* Queued MCAP request in some callback. */ /* We should not delete the mcl private data */ - g_free(op); + free_mcap_mdl_op(op); } else { /* This is not a queued request. It's safe */ /* delete the mcl private data here. */ - g_free(mcl->priv_data); - mcl->priv_data = NULL; + free_mcl_priv_data(mcl); } } @@ -1641,16 +1646,6 @@ static void mcap_connect_mdl_cb(GIOChannel *chan, GError *conn_err, cb(mdl, conn_err, user_data); } -static void mdl_io_destroy(gpointer data) -{ - struct mcap_mdl_op_cb *con = data; - - if (con->destroy) - con->destroy(con->user_data); - - g_free(con); -} - gboolean mcap_connect_mdl(struct mcap_mdl *mdl, uint8_t mode, uint16_t dcpsm, mcap_mdl_operation_cb connect_cb, @@ -1673,13 +1668,13 @@ gboolean mcap_connect_mdl(struct mcap_mdl *mdl, uint8_t mode, } con = g_new0(struct mcap_mdl_op_cb, 1); - con->mdl = mdl; + con->mdl = mcap_mdl_ref(mdl); con->cb.op = connect_cb; con->destroy = destroy; con->user_data = user_data; mdl->dc = bt_io_connect(BT_IO_L2CAP, mcap_connect_mdl_cb, con, - mdl_io_destroy, err, + (GDestroyNotify) free_mcap_mdl_op, err, BT_IO_OPT_SOURCE_BDADDR, &mdl->mcl->ms->src, BT_IO_OPT_DEST_BDADDR, &mdl->mcl->addr, BT_IO_OPT_PSM, dcpsm, @@ -1690,6 +1685,7 @@ gboolean mcap_connect_mdl(struct mcap_mdl *mdl, uint8_t mode, if (!mdl->dc) { DBG("MDL Connection error"); mdl->state = MDL_CLOSED; + mcap_mdl_unref(con->mdl); g_free(con); return FALSE; } @@ -2167,3 +2163,24 @@ gboolean mcap_set_data_chan_mode(struct mcap_instance *mi, uint8_t mode, return bt_io_set(mi->dcio, BT_IO_L2CAP, 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/health/mcap_internal.h b/health/mcap_internal.h index 76e5fb78a..ff3a6927f 100644 --- a/health/mcap_internal.h +++ b/health/mcap_internal.h @@ -84,6 +84,7 @@ struct mcap_instance { }; struct mcap_csp; +struct mcap_mdl_op_cb; struct mcap_mcl { struct mcap_instance *ms; /* MCAP instance where this MCL belongs */ @@ -94,7 +95,7 @@ struct mcap_mcl { MCLState state; /* Current MCL State */ MCLRole role; /* Initiator or acceptor of this MCL */ MCAPCtrl req; /* Request control flag */ - void *priv_data; /* Temporal data to manage responses */ + 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 */ @@ -118,6 +119,7 @@ struct mcap_mdl { uint16_t mdlid; /* MDL id */ uint8_t mdep_id; /* MCAP Data End Point */ MDLState state; /* MDL state */ + gint ref; /* References counter */ }; struct sync_info_ind_data { diff --git a/health/mcap_lib.h b/health/mcap_lib.h index c1e8e4b84..74a415f8f 100644 --- a/health/mcap_lib.h +++ b/health/mcap_lib.h @@ -155,6 +155,9 @@ gboolean mcap_mdl_abort(struct mcap_mdl *mdl, 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 *ms,