From f817fefff69cc44d5a2e1ba7d8ed9976fe3b4235 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 12 Aug 2007 21:34:50 +0000 Subject: Implement proper acceptor side functionality --- audio/a2dp.c | 30 ++++-- audio/avdtp.c | 306 +++++++++++++++++++++++++++++++++++++++++--------------- audio/avdtp.h | 9 +- audio/manager.c | 77 ++++++++++---- audio/manager.h | 2 +- audio/sink.c | 32 +++++- audio/sink.h | 3 +- 7 files changed, 342 insertions(+), 117 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index abd348c3..6d9009a8 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -36,6 +36,7 @@ #include "logging.h" #include "manager.h" #include "avdtp.h" +#include "sink.h" #include "a2dp.h" static DBusConnection *connection = NULL; @@ -46,15 +47,32 @@ static uint32_t source_record_id = 0; static struct avdtp_local_sep *sink_sep = NULL; static struct avdtp_local_sep *source_sep = NULL; -static gboolean setconf_ind(struct avdtp_local_sep *sep, +static gboolean setconf_ind(struct avdtp *session, + struct avdtp_local_sep *sep, struct avdtp_stream *stream, - uint8_t int_seid, GSList *caps, - uint8_t *err) + GSList *caps, uint8_t *err, + uint8_t *category) { - if (sep == sink_sep) + struct device *dev; + bdaddr_t addr; + + if (sep == sink_sep) { debug("SBC Sink: Set_Configuration_Ind"); - else - debug("SBC Source: Set_Configuration_Ind"); + return TRUE; + } + + debug("SBC Source: Set_Configuration_Ind"); + + avdtp_get_peers(session, NULL, &addr); + + dev = manager_device_connected(&addr, A2DP_SOURCE_UUID); + if (!dev) { + *err = AVDTP_UNSUPPORTED_CONFIGURATION; + *category = 0x00; + return FALSE; + } + + sink_new_stream(session, stream, dev); return TRUE; } diff --git a/audio/avdtp.c b/audio/avdtp.c index 07ca7d42..30e72761 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -175,6 +175,10 @@ struct close_resp { struct avdtp_header header; } __attribute__ ((packed)); +struct open_resp { + struct avdtp_header header; +} __attribute__ ((packed)); + struct stream_pause_resp { struct avdtp_header header; uint8_t rfa0:2; @@ -221,12 +225,14 @@ struct avdtp_stream { uint16_t mtu; struct avdtp *session; struct avdtp_local_sep *lsep; - struct avdtp_remote_sep *rsep; + uint8_t rseid; GSList *caps; + struct avdtp_service_capability *codec; avdtp_stream_state_cb cb; void *user_data; guint io; /* Transport GSource ID */ - guint close_timer; /* Waiting for other side to close transport */ + guint timer; /* Waiting for other side to close or open + the transport channel */ gboolean open_acp; /* If we are in ACT role for Open */ gboolean close_int; /* If we are in INT role for Close */ }; @@ -234,6 +240,7 @@ struct avdtp_stream { /* Structure describing an AVDTP connection between two devices */ struct avdtp { int ref; + int free_lock; bdaddr_t src; bdaddr_t dst; @@ -344,11 +351,30 @@ static gboolean stream_close_timeout(gpointer user_data) { struct avdtp_stream *stream = user_data; + debug("Timed out waiting for peer to close the transport channel"); + + stream->timer = 0; + close(stream->sock); return FALSE; } +static gboolean stream_open_timeout(gpointer user_data) +{ + struct avdtp_stream *stream = user_data; + + debug("Timed out waiting for peer to open the transport channel"); + + stream->timer = 0; + + stream->session->pending_open = NULL; + + avdtp_abort(stream->session, stream); + + return FALSE; +} + static gboolean disconnect_timeout(gpointer user_data) { struct avdtp *session = user_data; @@ -390,13 +416,48 @@ static void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id) } } +static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session, + uint8_t rseid) +{ + GSList *l; + + for (l = session->streams; l != NULL; l = g_slist_next(l)) { + struct avdtp_stream *stream = l->data; + + if (stream->rseid == rseid) + return stream; + } + + return NULL; +} + +static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid) +{ + GSList *l; + + for (l = seps; l != NULL; l = g_slist_next(l)) { + struct avdtp_remote_sep *sep = l->data; + + if (sep->seid == seid) + return sep; + } + + return NULL; +} + static void stream_free(struct avdtp_stream *stream) { + struct avdtp_remote_sep *rsep; + stream->lsep->info.inuse = 0; stream->lsep->stream = NULL; - stream->rsep->stream = NULL; - if (stream->close_timer) - g_source_remove(stream->close_timer); + + rsep = find_remote_sep(stream->session->seps, stream->rseid); + if (rsep) + rsep->stream = NULL; + + if (stream->timer) + g_source_remove(stream->timer); if (stream->caps) { g_slist_foreach(stream->caps, (GFunc) g_free, NULL); g_slist_free(stream->caps); @@ -427,6 +488,8 @@ static void avdtp_sep_set_state(struct avdtp *session, if (state == AVDTP_STATE_IDLE) { session->streams = g_slist_remove(session->streams, stream); stream_free(stream); + if (session->ref == 1 && !session->streams) + set_disconnect_timer(session); } } @@ -460,11 +523,15 @@ static void connection_lost(struct avdtp *session, int err) debug("Disconnected from %s", address); } + session->free_lock = 1; + finalize_discovery(session, err); g_slist_foreach(session->streams, (GFunc) release_stream, session); session->streams = NULL; + session->free_lock = 0; + if (session->sock >= 0) { close(session->sock); session->sock = -1; @@ -505,7 +572,8 @@ void avdtp_unref(struct avdtp *session) if (session->sock >= 0) set_disconnect_timer(session); - else /* Drop the local ref if we aren't connected */ + else if (!session->free_lock) /* Drop the local ref if we + aren't connected */ session->ref--; } @@ -574,6 +642,42 @@ static struct avdtp_local_sep *find_local_sep(uint8_t type, uint8_t media_type, return NULL; } +static GSList *caps_to_list(uint8_t *data, int size, + struct avdtp_service_capability **codec) +{ + GSList *caps; + int processed; + + for (processed = 0, caps = NULL; processed + 2 < size;) { + struct avdtp_service_capability *cap; + uint8_t length, category; + + category = data[0]; + length = data[1]; + + if (processed + 2 + length > size) { + error("Invalid capability data in getcap resp"); + break; + } + + cap = g_malloc(sizeof(struct avdtp_service_capability) + + length); + memcpy(cap, data, 2 + length); + + processed += 2 + length; + data += 2 + length; + + caps = g_slist_append(caps, cap); + + if (category == AVDTP_MEDIA_CODEC && + length >= + sizeof(struct avdtp_media_codec_capability)) + *codec = cap; + } + + return caps; +} + static void init_response(struct avdtp_header *rsp, struct avdtp_header *req, gboolean accept) { @@ -682,39 +786,62 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, { struct conf_rej rej; struct setconf_resp *rsp = (struct setconf_resp *) session->buf; - struct avdtp_local_sep *lsep; - gboolean ret; - uint8_t err; + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + uint8_t err, category = 0x00; if (size < sizeof(struct setconf_req)) { error("Too short getcap request"); return FALSE; } - lsep = find_local_sep_by_seid(req->acp_seid); - if (!lsep) { + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep) { err = AVDTP_BAD_ACP_SEID; goto failed; } - if (lsep->stream) { + if (sep->stream) { err = AVDTP_SEP_IN_USE; goto failed; } + + stream = g_new0(struct avdtp_stream, 1); + stream->session = session; + stream->lsep = sep; + stream->rseid = req->int_seid; + stream->caps = caps_to_list(req->caps, + size - sizeof(struct setconf_req), + &stream->codec); + stream->sock = -1; + + if (sep->ind && sep->ind->set_configuration) { + if (!sep->ind->set_configuration(session, sep, stream, + stream->caps, &err, + &category)) { + stream_free(stream); + goto failed; + } + } init_response(&rsp->header, &req->header, TRUE); - ret = avdtp_send(session, rsp, sizeof(struct setconf_req)); + if (!avdtp_send(session, rsp, sizeof(struct setconf_req))) { + stream_free(stream); + return FALSE; + } - if (ret) - avdtp_sep_set_state(session, lsep, AVDTP_STATE_CONFIGURED); + sep->stream = stream; + session->streams = g_slist_append(session->streams, stream); - return ret; + avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); + + return TRUE; failed: init_response(&rej.header, &req->header, FALSE); rej.error = err; - rej.category = 0x00; /* 0x00 means "not applicable" */ + rej.category = category; return avdtp_send(session, &rej, sizeof(rej)); } @@ -733,7 +860,52 @@ static gboolean avdtp_reconf_cmd(struct avdtp *session, struct seid_req *req, static gboolean avdtp_open_cmd(struct avdtp *session, struct seid_req *req, int size) { - return avdtp_unknown_cmd(session, (void *) req, size); + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + struct open_resp *rsp = (struct open_resp *) session->buf; + struct seid_rej rej; + uint8_t err; + + if (size < sizeof(struct seid_req)) { + error("Too short abort request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (sep->state != AVDTP_STATE_CONFIGURED) { + err = AVDTP_BAD_STATE; + goto failed; + } + + stream = sep->stream; + + if (sep->ind && sep->ind->open) { + if (!sep->ind->open(sep, stream, &err)) + goto failed; + } + + init_response(&rsp->header, &req->header, TRUE); + + if (!avdtp_send(session, rsp, sizeof(struct open_resp))) + return FALSE; + + stream->open_acp = TRUE; + session->pending_open = stream; + avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + stream->timer = g_timeout_add(REQ_TIMEOUT, stream_open_timeout, + stream); + + return TRUE; + +failed: + init_response(&rej.header, &req->header, FALSE); + rej.error = err; + return avdtp_send(session, &rej, sizeof(rej)); } static gboolean avdtp_start_cmd(struct avdtp *session, struct seid_req *req, @@ -750,7 +922,6 @@ static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, struct close_resp *rsp = (struct close_resp *) session->buf; struct seid_rej rej; uint8_t err; - gboolean ret; if (size < sizeof(struct seid_req)) { error("Too short abort request"); @@ -780,14 +951,13 @@ static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, init_response(&rsp->header, &req->header, TRUE); - ret = avdtp_send(session, rsp, sizeof(struct close_resp)); - if (ret == TRUE) { - stream->close_timer = g_timeout_add(REQ_TIMEOUT, - stream_close_timeout, - stream); - } + if (!avdtp_send(session, rsp, sizeof(struct close_resp))) + return FALSE; - return ret; + stream->timer = g_timeout_add(REQ_TIMEOUT, stream_close_timeout, + stream); + + return TRUE; failed: init_response(&rej.header, &req->header, FALSE); @@ -912,10 +1082,15 @@ static void handle_transport_connect(struct avdtp *session, int sock, session->pending_open = NULL; + if (stream->timer) { + g_source_remove(stream->timer); + stream->timer = 0; + } + stream->sock = sock; stream->mtu = mtu; - if (sep->cfm && sep->cfm->open) + if (!stream->open_acp && sep->cfm && sep->cfm->open) sep->cfm->open(sep, stream); channel = g_io_channel_unix_new(stream->sock); @@ -979,6 +1154,9 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, if (session->ref == 1 && !session->streams) set_disconnect_timer(session); + if (session->streams && session->dc_timer) + remove_disconnect_timer(session); + return TRUE; } @@ -1180,20 +1358,6 @@ static void queue_request(struct avdtp *session, struct pending_req *req, session->req_queue = g_slist_append(session->req_queue, req); } -static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid) -{ - GSList *l; - - for (l = seps; l != NULL; l = g_slist_next(l)) { - struct avdtp_remote_sep *sep = l->data; - - if (sep->seid == seid) - return sep; - } - - return NULL; -} - static gboolean request_timeout(gpointer user_data) { struct avdtp *session = user_data; @@ -1224,7 +1388,7 @@ static gboolean request_timeout(gpointer user_data) goto failed; } - stream = sep->stream; + stream = find_stream_by_rseid(session, seid); memset(&sreq, 0, sizeof(sreq)); init_request(&sreq.header, AVDTP_ABORT); @@ -1339,9 +1503,7 @@ static gboolean avdtp_get_capabilities_resp(struct avdtp *session, struct getcap_resp *resp, int size) { - int processed; struct avdtp_remote_sep *sep; - unsigned char *ptr; uint8_t seid; /* Check for minimum required packet size includes: @@ -1367,36 +1529,8 @@ static gboolean avdtp_get_capabilities_resp(struct avdtp *session, sep->codec = NULL; } - ptr = resp->caps; - processed = sizeof(struct getcap_resp); - - while (processed + 2 < size) { - struct avdtp_service_capability *cap; - uint8_t length, category; - - category = ptr[0]; - length = ptr[1]; - - if (processed + 2 + length > size) { - error("Invalid capability data in getcap resp"); - return FALSE; - } - - cap = g_malloc(sizeof(struct avdtp_service_capability) + - length); - memcpy(cap, ptr, 2 + length); - - processed += 2 + length; - ptr += 2 + length; - - sep->caps = g_slist_append(sep->caps, cap); - - if (category == AVDTP_MEDIA_CODEC && - length >= - sizeof(struct avdtp_media_codec_capability)) - sep->codec = cap; - - } + sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp), + &sep->codec); return TRUE; } @@ -1855,7 +1989,7 @@ int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_GET_CONFIGURATION); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, &req, sizeof(req)); } @@ -1883,7 +2017,7 @@ int avdtp_set_configuration(struct avdtp *session, new_stream->session = session; new_stream->lsep = lsep; - new_stream->rsep = rsep; + new_stream->rseid = rsep->seid; new_stream->caps = caps; /* Calculate total size of request */ @@ -1935,7 +2069,7 @@ int avdtp_reconfigure(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_GET_CONFIGURATION); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; return send_request(session, FALSE, NULL, &req, sizeof(req)); } @@ -1952,7 +2086,7 @@ int avdtp_open(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_OPEN); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, &req, sizeof(req)); } @@ -1969,7 +2103,7 @@ int avdtp_start(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_START); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, &req, sizeof(req)); } @@ -1987,7 +2121,7 @@ int avdtp_close(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_CLOSE); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; ret = send_request(session, FALSE, stream, &req, sizeof(req)); if (ret == 0) @@ -2009,7 +2143,7 @@ int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_SUSPEND); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; ret = send_request(session, FALSE, stream, &req, sizeof(req)); if (ret == 0) @@ -2032,7 +2166,7 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_ABORT); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; ret = send_request(session, FALSE, stream, &req, sizeof(req)); if (ret == 0) @@ -2254,6 +2388,14 @@ const char *avdtp_strerror(struct avdtp_error *err) } } +void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst) +{ + if (src) + bacpy(src, &session->src); + if (dst) + bacpy(dst, &session->dst); +} + int avdtp_init(void) { if (avdtp_server) diff --git a/audio/avdtp.h b/audio/avdtp.h index b4af9eb3..9c4f41e9 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -121,10 +121,11 @@ struct avdtp_sep_cfm { struct avdtp_sep_ind { gboolean (*get_capability) (struct avdtp_local_sep *sep, GSList **caps, uint8_t *err); - gboolean (*set_configuration) (struct avdtp_local_sep *lsep, + gboolean (*set_configuration) (struct avdtp *session, + struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - uint8_t int_seid, GSList *caps, - uint8_t *err); + GSList *caps, uint8_t *err, + uint8_t *category); gboolean (*get_configuration) (struct avdtp_local_sep *lsep, uint8_t *err); gboolean (*open) (struct avdtp_local_sep *lsep, @@ -194,6 +195,8 @@ int avdtp_unregister_sep(struct avdtp_local_sep *sep); const char *avdtp_strerror(struct avdtp_error *err); +void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst); + int avdtp_init(void); void avdtp_exit(void); diff --git a/audio/manager.c b/audio/manager.c index 995cdde5..e3c5a564 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -85,6 +85,7 @@ struct audio_sdp_data { static DBusConnection *connection = NULL; +static struct device *default_hs = NULL; static struct device *default_dev = NULL; static GSList *devices = NULL; @@ -132,6 +133,11 @@ static void remove_device(struct device *device) default_dev = NULL; } + if (device == default_hs) { + debug("Removing default headset"); + default_hs = NULL; + } + devices = g_slist_remove(devices, device); dbus_connection_destroy_object_path(connection, device->path); @@ -144,6 +150,9 @@ static gboolean add_device(struct device *device) default_dev = device; } + if (!default_hs && device->headset && !devices) + default_hs = device; + devices = g_slist_append(devices, device); return TRUE; @@ -580,16 +589,13 @@ static DBusHandlerResult resolve_services(DBusMessage *msg, return get_handles(GENERIC_AUDIO_UUID, sdp_data); } -struct device *manager_device_connected(bdaddr_t *bda) +struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) { struct device *device; const char *path; - gboolean created = FALSE; + gboolean headset = FALSE, created = FALSE; device = find_device(bda); - if (device && device->headset) - return device; - if (!device) { device = create_device(bda); if (!add_device(device)) { @@ -599,10 +605,26 @@ struct device *manager_device_connected(bdaddr_t *bda) created = TRUE; } - if (!device->headset) + if (!strcmp(uuid, HSP_AG_UUID) || !strcmp(uuid, HSP_AG_UUID)) { + if (device->headset) + return device; + device->headset = headset_init(device, NULL, 0); - if (!device->headset) + if (!device->headset) + return NULL; + + headset = TRUE; + } + else if (!strcmp(uuid, A2DP_SOURCE_UUID)) { + if (device->sink) + return device; + + device->sink = sink_init(device); + + if (!device->sink) + return NULL; + } else return NULL; path = device->path; @@ -616,17 +638,27 @@ struct device *manager_device_connected(bdaddr_t *bda) resolve_services(NULL, device); } - dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, - AUDIO_MANAGER_INTERFACE, - "HeadsetCreated", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); + if (headset) + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "HeadsetCreated", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + + if (headset && !default_hs) { + default_hs = device; + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DefaultHeadsetChanged", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + } if (!default_dev) { default_dev = device; dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, - "DefaultHeadsetChanged", + "DefaultDeviceChanged", DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); } @@ -1405,6 +1437,7 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) struct device *device; DBusMessage *auth; DBusPendingCall *pending; + headset_type_t type; if (cond & G_IO_NVAL) return FALSE; @@ -1425,7 +1458,15 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - device = manager_device_connected(&addr.rc_bdaddr); + if (chan == hs_server) { + type = SVC_HEADSET; + uuid = HSP_AG_UUID; + } else { + type = SVC_HANDSFREE; + uuid = HFP_AG_UUID; + } + + device = manager_device_connected(&addr.rc_bdaddr, uuid); if (!device) { close(cli_sk); return TRUE; @@ -1443,13 +1484,7 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - if (chan == hs_server) { - headset_set_type(device, SVC_HEADSET); - uuid = HSP_AG_UUID; - } else { - headset_set_type(device, SVC_HANDSFREE); - uuid = HFP_AG_UUID; - } + headset_set_type(device, type); auth = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", diff --git a/audio/manager.h b/audio/manager.h index 9fbc4940..0bfcad85 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -46,7 +46,7 @@ void audio_exit(void); uint32_t add_service_record(DBusConnection *conn, sdp_buf_t *buf); int remove_service_record(DBusConnection *conn, uint32_t rec_id); -struct device *manager_device_connected(bdaddr_t *bda); +struct device *manager_device_connected(bdaddr_t *bda, const char *uuid); struct device *manager_default_device(); diff --git a/audio/sink.c b/audio/sink.c index f8d56793..be762e39 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -53,6 +53,7 @@ struct sink { uint8_t state; struct pending_connect *c; DBusConnection *conn; + gboolean initiator; }; static void pending_connect_free(struct pending_connect *c) @@ -87,9 +88,13 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, avdtp_unref(sink->session); sink->session = NULL; } + sink->stream = NULL; c = sink->c; break; case AVDTP_STATE_CONFIGURED: + if (!sink->initiator) + break; + cmd_err = avdtp_open(sink->session, stream); if (cmd_err < 0) { error("Error on avdtp_open %s (%d)", strerror(-cmd_err), @@ -163,11 +168,10 @@ failed: if (new_state == AVDTP_STATE_IDLE) { avdtp_unref(sink->session); sink->session = NULL; + sink->stream = NULL; } } - - static void discovery_complete(struct avdtp *session, GSList *seps, int err, void *user_data) { @@ -205,6 +209,8 @@ static void discovery_complete(struct avdtp *session, GSList *seps, int err, goto failed; } + sink->initiator = TRUE; + avdtp_stream_set_cb(session, sink->stream, stream_state_changed, dev); return; @@ -299,7 +305,7 @@ static DBusHandlerResult sink_is_connected(DBusConnection *conn, if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - connected = (sink->state != AVDTP_STATE_IDLE); + connected = (sink->state >= AVDTP_STATE_CONFIGURED); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, DBUS_TYPE_INVALID); @@ -456,3 +462,23 @@ avdtp_state_t sink_get_state(void *device) return sink->state; } + +gboolean sink_new_stream(struct avdtp *session, struct avdtp_stream *stream, + void *dev) +{ + struct sink *sink = ((struct device *) (dev))->sink; + + if (sink->stream) + return FALSE; + + if (!sink->session) + sink->session = avdtp_ref(session); + + sink->stream = stream; + sink->initiator = FALSE; + + avdtp_stream_set_cb(session, stream, stream_state_changed, dev); + + return TRUE; +} + diff --git a/audio/sink.h b/audio/sink.h index b14a29a7..45745a19 100644 --- a/audio/sink.h +++ b/audio/sink.h @@ -29,10 +29,11 @@ struct sink; struct sink *sink_init(void *device); -void sink_new_stream(void *device, void *lsep); void sink_free(void *device); int sink_get_config(void *device, int sock, struct ipc_packet *req, int pkt_len, struct ipc_data_cfg **rsp, int *fd); gboolean sink_is_active(void *device); void sink_set_state(void *device, avdtp_state_t state); avdtp_state_t sink_get_state(void *device); +gboolean sink_new_stream(struct avdtp *session, struct avdtp_stream *stream, + void *dev); -- cgit