summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--audio/a2dp.c30
-rw-r--r--audio/avdtp.c306
-rw-r--r--audio/avdtp.h9
-rw-r--r--audio/manager.c77
-rw-r--r--audio/manager.h2
-rw-r--r--audio/sink.c32
-rw-r--r--audio/sink.h3
7 files changed, 342 insertions, 117 deletions
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);