summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--audio/a2dp.c460
-rw-r--r--audio/a2dp.h16
-rw-r--r--audio/avdtp.c10
-rw-r--r--audio/avdtp.h4
-rw-r--r--audio/device.c25
-rw-r--r--audio/device.h5
-rw-r--r--audio/headset.c33
-rw-r--r--audio/headset.h5
-rw-r--r--audio/sink.c325
-rw-r--r--audio/sink.h3
-rw-r--r--audio/unix.c215
11 files changed, 736 insertions, 365 deletions
diff --git a/audio/a2dp.c b/audio/a2dp.c
index b8431bc6..5302fb2d 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -42,16 +42,67 @@
#include "sink.h"
#include "a2dp.h"
+#ifndef MIN
+# define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+struct a2dp_sep {
+ struct avdtp_local_sep *sep;
+ struct avdtp_stream *stream;
+ struct device *used_by;
+ uint32_t record_id;
+ gboolean start_requested;
+ gboolean suspending;
+ gboolean starting;
+};
+
+struct a2dp_stream_cb {
+ a2dp_stream_cb_t cb;
+ void *user_data;
+ int id;
+};
+
+struct a2dp_stream_setup {
+ struct avdtp *session;
+ struct device *dev;
+ struct avdtp_stream *stream;
+ gboolean start;
+ gboolean canceled;
+ GSList *cb;
+};
+
static DBusConnection *connection = NULL;
-static uint32_t sink_record_id = 0;
-static uint32_t source_record_id = 0;
+static struct a2dp_sep sink = { NULL, NULL, 0 };
+static struct a2dp_sep source = { NULL, NULL, 0 };
-static struct avdtp_local_sep *sink_sep = NULL;
-static struct avdtp_local_sep *source_sep = NULL;
+static struct a2dp_stream_setup *setup = NULL;
-static struct avdtp *start_session = NULL;
-static struct avdtp_stream *start_stream = NULL;
+static void stream_setup_free(struct a2dp_stream_setup *s)
+{
+ if (s->session)
+ avdtp_unref(s->session);
+ g_slist_foreach(s->cb, (GFunc) g_free, NULL);
+ g_slist_free(s->cb);
+ g_free(s);
+ setup = NULL;
+}
+
+static void setup_callback(struct a2dp_stream_cb *cb,
+ struct a2dp_stream_setup *s)
+{
+ cb->cb(s->session, s->dev, s->stream, cb->user_data);
+}
+
+static void finalize_stream_setup(struct a2dp_stream_setup *s)
+{
+ g_slist_foreach(setup->cb, (GFunc) setup_callback, setup);
+ stream_setup_free(setup);
+}
static gboolean setconf_ind(struct avdtp *session,
struct avdtp_local_sep *sep,
@@ -62,7 +113,7 @@ static gboolean setconf_ind(struct avdtp *session,
struct device *dev;
bdaddr_t addr;
- if (sep == sink_sep) {
+ if (sep == sink.sep) {
debug("SBC Sink: Set_Configuration_Ind");
return TRUE;
}
@@ -78,6 +129,8 @@ static gboolean setconf_ind(struct avdtp *session,
return FALSE;
}
+ source.stream = stream;
+
sink_new_stream(dev, session, stream);
return TRUE;
@@ -89,7 +142,7 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_service_capability *media_transport, *media_codec;
struct sbc_codec_cap sbc_cap;
- if (sep == sink_sep)
+ if (sep == sink.sep)
debug("SBC Sink: Get_Capability_Ind");
else
debug("SBC Source: Get_Capability_Ind");
@@ -140,16 +193,33 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep,
static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream)
{
- if (sep == sink_sep)
+ int err;
+
+ if (sep == sink.sep) {
debug("SBC Sink: Set_Configuration_Cfm");
- else
- debug("SBC Source: Set_Configuration_Cfm");
+ return;
+ }
+
+ debug("SBC Source: Set_Configuration_Cfm");
+
+ source.stream = stream;
+
+ if (!setup)
+ return;
+
+ err = avdtp_open(session, stream);
+ if (err < 0) {
+ error("Error on avdtp_open %s (%d)", strerror(-err),
+ -err);
+ setup->stream = FALSE;
+ finalize_stream_setup(setup);
+ }
}
static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
uint8_t *err)
{
- if (sep == sink_sep)
+ if (sep == sink.sep)
debug("SBC Sink: Get_Configuration_Ind");
else
debug("SBC Source: Get_Configuration_Ind");
@@ -159,7 +229,7 @@ static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream)
{
- if (sep == sink_sep)
+ if (sep == sink.sep)
debug("SBC Sink: Set_Configuration_Cfm");
else
debug("SBC Source: Set_Configuration_Cfm");
@@ -168,7 +238,7 @@ static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, uint8_t *err)
{
- if (sep == sink_sep)
+ if (sep == sink.sep)
debug("SBC Sink: Open_Ind");
else
debug("SBC Source: Open_Ind");
@@ -178,28 +248,35 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream)
{
- int err;
-
- if (sep == sink_sep)
+ if (sep == sink.sep)
debug("SBC Sink: Open_Cfm");
else
debug("SBC Source: Open_Cfm");
- if (session != start_session || stream != start_stream)
+ if (!setup)
return;
- start_session = NULL;
- start_stream = NULL;
+ if (setup->canceled) {
+ avdtp_close(session, stream);
+ stream_setup_free(setup);
+ return;
+ }
+
+ if (setup->start) {
+ if (avdtp_start(session, stream) == 0)
+ return;
- err = avdtp_start(session, stream);
- if (err < 0)
- error("Error on avdtp_start %s (%d)", strerror(-err), err);
+ error("avdtp_start failed");
+ setup->stream = NULL;
+ }
+
+ finalize_stream_setup(setup);
}
static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, uint8_t *err)
{
- if (sep == sink_sep)
+ if (sep == sink.sep)
debug("SBC Sink: Start_Ind");
else
debug("SBC Source: Start_Ind");
@@ -213,16 +290,27 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep,
static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream)
{
- if (sep == sink_sep)
+ if (sep == sink.sep)
debug("SBC Sink: Start_Cfm");
else
debug("SBC Source: Start_Cfm");
+
+ if (!setup)
+ return;
+
+ if (setup->canceled) {
+ avdtp_close(session, stream);
+ stream_setup_free(setup);
+ return;
+ }
+
+ finalize_stream_setup(setup);
}
static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, uint8_t *err)
{
- if (sep == sink_sep)
+ if (sep == sink.sep)
debug("SBC Sink: Suspend_Ind");
else
debug("SBC Source: Suspend_Ind");
@@ -232,45 +320,68 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep,
static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream)
{
- if (sep == sink_sep)
+ if (sep == sink.sep) {
debug("SBC Sink: Suspend_Cfm");
- else
- debug("SBC Source: Suspend_Cfm");
+ return;
+ }
+
+ debug("SBC Source: Suspend_Cfm");
+
+ source.suspending = FALSE;
+
+ if (source.start_requested) {
+ avdtp_start(session, stream);
+ source.start_requested = FALSE;
+ }
}
static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, uint8_t *err)
{
- if (sep == sink_sep)
+ if (sep == sink.sep) {
debug("SBC Sink: Close_Ind");
- else
- debug("SBC Source: Close_Ind");
+ return TRUE;
+ }
+
+ debug("SBC Source: Close_Ind");
+
+ source.stream = NULL;
+
return TRUE;
}
static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream)
{
- if (sep == sink_sep)
+ if (sep == sink.sep) {
debug("SBC Sink: Close_Cfm");
- else
- debug("SBC Source: Close_Cfm");
+ return;
+ }
+
+ debug("SBC Source: Close_Cfm");
+
+ source.stream = NULL;
}
static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, uint8_t *err)
{
- if (sep == sink_sep)
+ if (sep == sink.sep) {
debug("SBC Sink: Abort_Ind");
- else
- debug("SBC Source: Abort_Ind");
+ return TRUE;
+ }
+
+ debug("SBC Source: Abort_Ind");
+
+ source.stream = NULL;
+
return TRUE;
}
static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream)
{
- if (sep == sink_sep)
+ if (sep == sink.sep)
debug("SBC Sink: Abort_Cfm");
else
debug("SBC Source: Abort_Cfm");
@@ -279,7 +390,7 @@ static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
uint8_t *err)
{
- if (sep == sink_sep)
+ if (sep == sink.sep)
debug("SBC Sink: ReConfigure_Ind");
else
debug("SBC Source: ReConfigure_Ind");
@@ -288,7 +399,7 @@ static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep)
{
- if (sep == sink_sep)
+ if (sep == sink.sep)
debug("SBC Sink: ReConfigure_Cfm");
else
debug("SBC Source: ReConfigure_Cfm");
@@ -400,10 +511,10 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source
avdtp_init();
if (enable_sink) {
- source_sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE,
- AVDTP_MEDIA_TYPE_AUDIO,
- &ind, &cfm);
- if (source_sep == NULL)
+ source.sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE,
+ AVDTP_MEDIA_TYPE_AUDIO,
+ &ind, &cfm);
+ if (source.sep == NULL)
return -1;
if (a2dp_source_record(&buf) < 0) {
@@ -411,19 +522,19 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source
return -1;
}
- source_record_id = add_service_record(conn, &buf);
+ source.record_id = add_service_record(conn, &buf);
free(buf.data);
- if (!source_record_id) {
+ if (!source.record_id) {
error("Unable to register A2DP Source service record");
return -1;
}
}
if (enable_source) {
- sink_sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK,
+ sink.sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK,
AVDTP_MEDIA_TYPE_AUDIO,
&ind, &cfm);
- if (sink_sep == NULL)
+ if (sink.sep == NULL)
return -1;
if (a2dp_sink_record(&buf) < 0) {
@@ -431,9 +542,9 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source
return -1;
}
- sink_record_id = add_service_record(conn, &buf);
+ sink.record_id = add_service_record(conn, &buf);
free(buf.data);
- if (!sink_record_id) {
+ if (!sink.record_id) {
error("Unable to register A2DP Sink service record");
return -1;
}
@@ -444,20 +555,24 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source
void a2dp_exit()
{
- if (sink_sep)
- avdtp_unregister_sep(sink_sep);
+ if (sink.sep) {
+ avdtp_unregister_sep(sink.sep);
+ sink.sep = NULL;
+ }
- if (source_sep)
- avdtp_unregister_sep(source_sep);
+ if (source.sep) {
+ avdtp_unregister_sep(source.sep);
+ source.sep = NULL;
+ }
- if (source_record_id) {
- remove_service_record(connection, source_record_id);
- source_record_id = 0;
+ if (source.record_id) {
+ remove_service_record(connection, source.record_id);
+ source.record_id = 0;
}
- if (sink_record_id) {
- remove_service_record(connection, sink_record_id);
- sink_record_id = 0;
+ if (sink.record_id) {
+ remove_service_record(connection, sink.record_id);
+ sink.record_id = 0;
}
dbus_connection_unref(connection);
@@ -561,7 +676,7 @@ static gboolean select_sbc_params(struct sbc_codec_cap *cap,
else if (supported->allocation_method & A2DP_ALLOCATION_SNR)
cap->allocation_method = A2DP_ALLOCATION_SNR;
- min_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),
+ min_bitpool = MAX(default_bitpool(cap->frequency, cap->channel_mode),
supported->min_bitpool);
max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),
supported->max_bitpool);
@@ -685,10 +800,223 @@ gboolean a2dp_get_config(struct avdtp_stream *stream,
return TRUE;
}
-void a2dp_start_stream_when_opened(struct avdtp *session,
- struct avdtp_stream *stream)
+static void discovery_complete(struct avdtp *session, GSList *seps, int err,
+ void *user_data)
{
- start_session = session;
- start_stream = stream;
+ struct avdtp_local_sep *lsep;
+ struct avdtp_remote_sep *rsep;
+ GSList *caps = NULL;
+
+ if (err < 0) {
+ error("Discovery failed: %s (%d)", strerror(-err), -err);
+ setup->stream = NULL;
+ finalize_stream_setup(setup);
+ return;
+ }
+ debug("Discovery complete");
+
+ if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+ A2DP_CODEC_SBC, &lsep, &rsep) < 0) {
+ error("No matching ACP and INT SEPs found");
+ finalize_stream_setup(setup);
+ return;
+ }
+
+ if (!a2dp_select_capabilities(rsep, &caps)) {
+ error("Unable to select remote SEP capabilities");
+ finalize_stream_setup(setup);
+ return;
+ }
+
+ err = avdtp_set_configuration(session, rsep, lsep, caps,
+ &setup->stream);
+ if (err < 0) {
+ error("avdtp_set_configuration: %s", strerror(-err));
+ finalize_stream_setup(setup);
+ return;
+ }
+
+ /* Notify sink.c of the new stream */
+ sink_new_stream(setup->dev, session, setup->stream);
+}
+
+gboolean a2dp_source_cancel_stream(int id)
+{
+ struct a2dp_stream_cb *cb_data;
+ GSList *l;
+
+ if (!setup)
+ return FALSE;
+
+ for (cb_data = NULL, l = setup->cb; l != NULL; l = g_slist_next(l)) {
+ struct a2dp_stream_cb *cb = l->data;
+
+ if (cb->id == id) {
+ cb_data = cb;
+ break;
+ }
+ }
+
+ if (!cb_data)
+ return FALSE;
+
+ setup->cb = g_slist_remove(setup->cb, cb_data);
+
+ if (!setup->cb)
+ setup->canceled = TRUE;
+
+ return TRUE;
+}
+
+int a2dp_source_request_stream(struct avdtp *session, struct device *dev,
+ gboolean start, a2dp_stream_cb_t cb,
+ void *user_data)
+{
+ struct a2dp_stream_cb *cb_data;
+ static unsigned int cb_id = 0;
+
+ cb_data = g_new(struct a2dp_stream_cb, 1);
+ cb_data->cb = cb;
+ cb_data->user_data = user_data;
+ cb_data->id = cb_id++;
+
+ if (setup) {
+ setup->canceled = FALSE;
+ setup->cb = g_slist_append(setup->cb, cb_data);
+ if (start)
+ setup->start = TRUE;
+ return cb_data->id;
+ }
+
+ setup = g_new0(struct a2dp_stream_setup, 1);
+ setup->session = avdtp_ref(session);
+ setup->dev = dev;
+ setup->cb = g_slist_append(setup->cb, cb_data);
+ setup->start = start;
+ setup->stream = source.stream;
+
+ switch (avdtp_sep_get_state(source.sep)) {
+ case AVDTP_STATE_IDLE:
+ if (avdtp_discover(session, discovery_complete, setup) < 0)
+ goto failed;
+ break;
+ case AVDTP_STATE_OPEN:
+ if (!start) {
+ g_idle_add((GSourceFunc) finalize_stream_setup, setup);
+ break;
+ }
+ if (source.starting)
+ break;
+ if (avdtp_start(session, source.stream) < 0)
+ goto failed;
+ break;
+ case AVDTP_STATE_STREAMING:
+ if (!start || !source.suspending) {
+ g_idle_add((GSourceFunc) finalize_stream_setup, setup);
+ return cb_data->id;
+ }
+ source.start_requested = TRUE;
+ break;
+ default:
+ goto failed;
+ }
+
+ return cb_data->id;
+
+failed:
+ stream_setup_free(setup);
+ cb_id--;
+ return -1;
+}
+
+gboolean a2dp_source_lock(struct device *dev, struct avdtp *session)
+{
+ if (source.used_by)
+ return FALSE;
+
+ source.used_by = dev;
+
+ return TRUE;
+}
+
+gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session)
+{
+ avdtp_state_t state;
+
+ if (!source.sep)
+ return FALSE;
+
+ if (source.used_by != dev)
+ return FALSE;
+
+ state = avdtp_sep_get_state(source.sep);
+
+ source.used_by = NULL;
+
+ if (!source.stream || state == AVDTP_STATE_IDLE)
+ return TRUE;
+
+ switch (state) {
+ case AVDTP_STATE_OPEN:
+ /* Set timer here */
+ break;
+ case AVDTP_STATE_STREAMING:
+ if (avdtp_suspend(session, source.stream) == 0)
+ source.suspending = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session)
+{
+ avdtp_state_t state;
+
+ if (!source.sep)
+ return FALSE;
+
+ if (source.used_by != dev)
+ return FALSE;
+
+ state = avdtp_sep_get_state(source.sep);
+
+ if (!source.stream || state != AVDTP_STATE_STREAMING)
+ return TRUE;
+
+ if (avdtp_suspend(session, source.stream) == 0) {
+ source.suspending = TRUE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session)
+{
+ avdtp_state_t state;
+
+ if (!source.sep)
+ return FALSE;
+
+ if (source.used_by != dev)
+ return FALSE;
+
+ state = avdtp_sep_get_state(source.sep);
+
+ if (state < AVDTP_STATE_OPEN) {
+ error("a2dp_source_start_stream: no stream open");
+ return FALSE;
+ }
+
+ if (state == AVDTP_STATE_STREAMING)
+ return TRUE;
+
+ if (avdtp_start(session, source.stream) < 0)
+ return FALSE;
+
+ return TRUE;
}
diff --git a/audio/a2dp.h b/audio/a2dp.h
index 23416988..3617afc4 100644
--- a/audio/a2dp.h
+++ b/audio/a2dp.h
@@ -58,6 +58,10 @@ struct sbc_codec_cap {
uint8_t max_bitpool;
} __attribute__ ((packed));
+typedef void (*a2dp_stream_cb_t) (struct avdtp *session, struct device *dev,
+ struct avdtp_stream *stream,
+ void *user_data);
+
int a2dp_init(DBusConnection *conn, gboolean enable_sink,
gboolean enable_source);
void a2dp_exit(void);
@@ -67,5 +71,13 @@ gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps);
gboolean a2dp_get_config(struct avdtp_stream *stream,
struct ipc_data_cfg **cfg, int *fd);
-void a2dp_start_stream_when_opened(struct avdtp *session,
- struct avdtp_stream *stream);
+int a2dp_source_request_stream(struct avdtp *session, struct device *dev,
+ gboolean start, a2dp_stream_cb_t cb,
+ void *user_data);
+gboolean a2dp_source_cancel_stream(int id);
+
+gboolean a2dp_source_lock(struct device *dev, struct avdtp *session);
+gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session);
+gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session);
+gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session);
+
diff --git a/audio/avdtp.c b/audio/avdtp.c
index eb00f8bb..7194177f 100644
--- a/audio/avdtp.c
+++ b/audio/avdtp.c
@@ -1957,6 +1957,11 @@ struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst)
return avdtp_ref(session);
}
+gboolean avdtp_is_connected(bdaddr_t *src, bdaddr_t *dst)
+{
+ return find_session(src, dst) == NULL ? FALSE : TRUE;
+}
+
gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
uint16_t *mtu, GSList **caps)
{
@@ -2489,6 +2494,11 @@ const char *avdtp_strerror(struct avdtp_error *err)
}
}
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep)
+{
+ return sep->state;
+}
+
void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst)
{
if (src)
diff --git a/audio/avdtp.h b/audio/avdtp.h
index f7ca6308..0043c480 100644
--- a/audio/avdtp.h
+++ b/audio/avdtp.h
@@ -155,6 +155,8 @@ struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst);
void avdtp_unref(struct avdtp *session);
struct avdtp *avdtp_ref(struct avdtp *session);
+gboolean avdtp_is_connected(bdaddr_t *src, bdaddr_t *dst);
+
struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
void *data, int size);
@@ -196,6 +198,8 @@ int avdtp_get_seps(struct avdtp *session, uint8_t type, uint8_t media,
int avdtp_unregister_sep(struct avdtp_local_sep *sep);
+avdtp_state_t avdtp_sep_get_state(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);
diff --git a/audio/device.c b/audio/device.c
index fe3af8cb..2a6498ee 100644
--- a/audio/device.c
+++ b/audio/device.c
@@ -328,21 +328,7 @@ void device_finish_sdp_transaction(struct device *dev)
dbus_message_unref(reply);
}
-int device_get_config(struct device *dev, int sock, struct ipc_packet *req,
- int pkt_len, struct ipc_data_cfg **rsp, int *fd)
-{
- if (dev->sink && sink_is_active(dev))
- return sink_get_config(dev, sock, req, pkt_len, rsp, fd);
- else if (dev->headset && headset_is_active(dev))
- return headset_get_config(dev, sock, req, pkt_len, rsp, fd);
- else if (dev->sink)
- return sink_get_config(dev, sock, req, pkt_len, rsp, fd);
- else if (dev->headset)
- return headset_get_config(dev, sock, req, pkt_len, rsp, fd);
-
- return -EINVAL;
-}
-
+#if 0
static avdtp_state_t ipc_to_avdtp_state(uint8_t ipc_state)
{
switch (ipc_state) {
@@ -379,14 +365,7 @@ static headset_state_t ipc_to_hs_state(uint8_t ipc_state)
return HEADSET_STATE_DISCONNECTED;
}
}
-
-void device_set_state(struct device *dev, uint8_t state)
-{
- if (dev->sink && sink_is_active(dev))
- sink_set_state(dev, ipc_to_avdtp_state(state));
- else if (dev->headset && headset_is_active(dev))
- headset_set_state(dev, ipc_to_hs_state(state));
-}
+#endif
static uint8_t avdtp_to_ipc_state(avdtp_state_t state)
{
diff --git a/audio/device.h b/audio/device.h
index d9a42ca1..a527c096 100644
--- a/audio/device.h
+++ b/audio/device.h
@@ -75,9 +75,4 @@ int device_remove_stored(struct device *dev);
void device_finish_sdp_transaction(struct device *device);
-int device_get_config(struct device *dev, int sock, struct ipc_packet *req,
- int pkt_len, struct ipc_data_cfg **rsp, int *fd);
-
-void device_set_state(struct device *dev, uint8_t state);
-
uint8_t device_get_state(struct device *dev);
diff --git a/audio/headset.c b/audio/headset.c
index b0e67ba2..056f9e18 100644
--- a/audio/headset.c
+++ b/audio/headset.c
@@ -100,6 +100,8 @@ struct headset {
int sp_gain;
int mic_gain;
+
+ gboolean locked;
};
static int rfcomm_connect(struct device *device, struct pending_connect *c);
@@ -1570,3 +1572,34 @@ gboolean headset_is_active(struct device *dev)
return FALSE;
}
+
+gboolean headset_lock(struct device *dev, void *data)
+{
+ struct headset *hs = dev->headset;
+
+ if (hs->locked)
+ return FALSE;
+
+ hs->locked = TRUE;
+
+ return TRUE;
+}
+
+gboolean headset_unlock(struct device *dev, void *data)
+{
+ struct headset *hs = dev->headset;
+
+ hs->locked = FALSE;
+
+ return TRUE;
+}
+
+gboolean headset_suspend(struct device *dev, void *data)
+{
+ return TRUE;
+}
+
+gboolean headset_play(struct device *dev, void *data)
+{
+ return TRUE;
+}
diff --git a/audio/headset.h b/audio/headset.h
index 2a65d136..29e2e496 100644
--- a/audio/headset.h
+++ b/audio/headset.h
@@ -68,3 +68,8 @@ void headset_set_state(struct device *dev, headset_state_t state);
int headset_get_channel(struct device *dev);
gboolean headset_is_active(struct device *dev);
+
+gboolean headset_lock(struct device *dev, void *data);
+gboolean headset_unlock(struct device *dev, void *data);
+gboolean headset_suspend(struct device *dev, void *data);
+gboolean headset_play(struct device *dev, void *data);
diff --git a/audio/sink.c b/audio/sink.c
index fe8b6393..0f69bbf6 100644
--- a/audio/sink.c
+++ b/audio/sink.c
@@ -46,29 +46,29 @@
#include "sink.h"
struct pending_request {
+ DBusConnection *conn;
DBusMessage *msg;
- struct ipc_packet *pkt;
- int pkt_len;
- int sock;
+ int id;
};
struct sink {
struct avdtp *session;
struct avdtp_stream *stream;
uint8_t state;
- struct pending_request *c;
+ struct pending_request *connect;
+ struct pending_request *disconnect;
DBusConnection *conn;
gboolean initiator;
gboolean suspending;
};
-static void pending_connect_free(struct pending_request *c)
+static void pending_request_free(struct pending_request *pending)
{
- if (c->pkt)
- g_free(c->pkt);
- if (c->msg)
- dbus_message_unref(c->msg);
- g_free(c);
+ if (pending->conn)
+ dbus_connection_unref(pending->conn);
+ if (pending->msg)
+ dbus_message_unref(pending->msg);
+ g_free(pending);
}
void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state,
@@ -77,12 +77,9 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state,
{
struct device *dev = user_data;
struct sink *sink = dev->sink;
- struct pending_request *c = NULL;
- DBusMessage *reply;
- int cmd_err;
if (err)
- goto failed;
+ return;
switch (new_state) {
case AVDTP_STATE_IDLE:
@@ -90,152 +87,64 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state,
AUDIO_SINK_INTERFACE,
"Disconnected",
DBUS_TYPE_INVALID);
+ if (sink->disconnect) {
+ DBusMessage *reply;
+ struct pending_request *p;
+
+ p = sink->disconnect;
+ sink->disconnect = NULL;
+
+ reply = dbus_message_new_method_return(p->msg);
+ send_message_and_unref(p->conn, reply);
+ pending_request_free(p);
+ }
+
if (sink->session) {
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),
- cmd_err);
- goto failed;
- }
-
- if (sink->c && sink->c->pkt)
- a2dp_start_stream_when_opened(sink->session, stream);
break;
case AVDTP_STATE_OPEN:
- sink->suspending = FALSE;
-
if (old_state == AVDTP_STATE_CONFIGURED)
dbus_connection_emit_signal(dev->conn, dev->path,
AUDIO_SINK_INTERFACE,
"Connected",
DBUS_TYPE_INVALID);
-
- if (!sink->initiator)
- break;
-
- if (!(sink->c && sink->c->pkt))
- c = sink->c;
-
break;
+ case AVDTP_STATE_CONFIGURED:
case AVDTP_STATE_STREAMING:
- c = sink->c;
- break;
case AVDTP_STATE_CLOSING:
- break;
case AVDTP_STATE_ABORTING:
+ default:
break;
}
sink->state = new_state;
-
- if (c) {
- sink->c = NULL;
-
- if (c->msg) {
- reply = dbus_message_new_method_return(c->msg);
- send_message_and_unref(dev->conn, reply);
- }
- if (c->pkt) {
- struct ipc_data_cfg *rsp;
- int ret, fd;
-
- ret = sink_get_config(dev, c->sock, c->pkt,
- c->pkt_len, &rsp, &fd);
- if (ret == 0) {
- unix_send_cfg(c->sock, rsp, fd);
- g_free(rsp);
- }
- else
- unix_send_cfg(c->sock, NULL, -1);
- }
-
- pending_connect_free(c);
- }
-
- return;
-
-failed:
- if (sink->c) {
- if (sink->c->msg && err)
- err_failed(dev->conn, sink->c->msg,
- avdtp_strerror(err));
-
- pending_connect_free(sink->c);
- sink->c = NULL;
- }
-
- 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)
+static void stream_setup_complete(struct avdtp *session, struct device *dev,
+ struct avdtp_stream *stream,
+ void *user_data)
{
- struct device *dev = user_data;
struct sink *sink = dev->sink;
- struct avdtp_local_sep *lsep;
- struct avdtp_remote_sep *rsep;
- GSList *caps = NULL;
- const char *err_str = NULL;
-
- if (err < 0) {
- error("Discovery failed");
- err_str = strerror(-err);
- goto failed;
- }
-
- debug("Discovery complete");
-
- if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
- A2DP_CODEC_SBC, &lsep, &rsep) < 0) {
- err_str = "No matching ACP and INT SEPs found";
- goto failed;
- }
+ struct pending_request *pending;
- if (!a2dp_select_capabilities(rsep, &caps)) {
- err_str = "Unable to select remote SEP capabilities";
- goto failed;
- }
+ pending = sink->connect;
+ sink->connect = NULL;
- err = avdtp_set_configuration(session, rsep, lsep, caps,
- &sink->stream);
- if (err < 0) {
- error("avdtp_set_configuration: %s", strerror(-err));
- err_str = "Unable to set configuration";
- goto failed;
+ if (stream) {
+ DBusMessage *reply;
+ reply = dbus_message_new_method_return(pending->msg);
+ send_message_and_unref(pending->conn, reply);
}
-
- sink->initiator = TRUE;
-
- avdtp_stream_set_cb(session, sink->stream, stream_state_changed, dev);
-
- return;
-
-failed:
- error("%s", err_str);
- if (sink->c) {
- if (sink->c->msg)
- err_failed(dev->conn, sink->c->msg, err_str);
- pending_connect_free(sink->c);
- sink->c = NULL;
- }
- if (sink->session) {
+ else {
+ err_failed(pending->conn, pending->msg, "Stream setup failed");
avdtp_unref(sink->session);
sink->session = NULL;
}
+
+ pending_request_free(pending);
}
static DBusHandlerResult sink_connect(DBusConnection *conn,
@@ -243,31 +152,36 @@ static DBusHandlerResult sink_connect(DBusConnection *conn,
{
struct device *dev = data;
struct sink *sink = dev->sink;
- struct pending_request *c;
- int err;
+ struct pending_request *pending;
+ int id;
if (!sink->session)
sink->session = avdtp_get(&dev->src, &dev->dst);
- if (sink->c)
+ if (sink->connect || sink->disconnect)
return err_connect_failed(conn, msg, "Connect in progress");
if (sink->state >= AVDTP_STATE_OPEN)
return err_already_connected(conn, msg);
- c = g_new0(struct pending_request, 1);
- c->msg = dbus_message_ref(msg);
- sink->c = c;
+ pending = g_new0(struct pending_request, 1);
+ pending->conn = dbus_connection_ref(conn);
+ pending->msg = dbus_message_ref(msg);
+ sink->connect = pending;
- err = avdtp_discover(sink->session, discovery_complete, data);
- if (err < 0) {
- pending_connect_free(c);
- sink->c = NULL;
+ id = a2dp_source_request_stream(sink->session, dev, FALSE,
+ stream_setup_complete, pending);
+ if (id < 0) {
+ pending_request_free(pending);
+ sink->connect = NULL;
avdtp_unref(sink->session);
sink->session = NULL;
- return err_connect_failed(conn, msg, strerror(err));
+ return err_connect_failed(conn, msg,
+ "Failed to request a stream");
}
+ pending->id = id;
+
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -276,27 +190,32 @@ static DBusHandlerResult sink_disconnect(DBusConnection *conn,
{
struct device *device = data;
struct sink *sink = device->sink;
- struct pending_request *c;
+ struct pending_request *pending;
int err;
if (!sink->session)
return err_not_connected(conn, msg);
- if (sink->c)
+ if (sink->connect || sink->disconnect)
return err_failed(conn, msg, strerror(EBUSY));
if (sink->state < AVDTP_STATE_OPEN) {
+ DBusMessage *reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
avdtp_unref(sink->session);
sink->session = NULL;
- } else {
- err = avdtp_close(sink->session, sink->stream);
- if (err < 0)
- return err_failed(conn, msg, strerror(-err));
+ return send_message_and_unref(conn, reply);
}
- c = g_new0(struct pending_request, 1);
- c->msg = dbus_message_ref(msg);
- sink->c = c;
+ err = avdtp_close(sink->session, sink->stream);
+ if (err < 0)
+ return err_failed(conn, msg, strerror(-err));
+
+ pending = g_new0(struct pending_request, 1);
+ pending->conn = dbus_connection_ref(conn);
+ pending->msg = dbus_message_ref(msg);
+ sink->disconnect = pending;
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -355,66 +274,16 @@ void sink_free(struct device *dev)
if (sink->session)
avdtp_unref(sink->session);
- if (sink->c)
- pending_connect_free(sink->c);
+ if (sink->connect)
+ pending_request_free(sink->connect);
+
+ if (sink->disconnect)
+ pending_request_free(sink->disconnect);
g_free(sink);
dev->sink = NULL;
}
-int sink_get_config(struct device *dev, int sock, struct ipc_packet *req,
- int pkt_len, struct ipc_data_cfg **rsp, int *fd)
-{
- struct sink *sink = dev->sink;
- int err;
- struct pending_request *c = NULL;
-
- if (!sink->suspending && sink->state == AVDTP_STATE_STREAMING)
- goto proceed;
-
- if (sink->c) {
- error("sink_get_config: another request already in progress");
- return -EBUSY;
- }
-
- if (!sink->session)
- sink->session = avdtp_get(&dev->src, &dev->dst);
-
- c = g_new0(struct pending_request, 1);
- c->sock = sock;
- c->pkt = g_malloc(pkt_len);
- memcpy(c->pkt, req, pkt_len);
-
- if (sink->state == AVDTP_STATE_IDLE)
- err = avdtp_discover(sink->session, discovery_complete, dev);
- else if (sink->state < AVDTP_STATE_STREAMING)
- err = avdtp_start(sink->session, sink->stream);
- else if (sink->suspending)
- err = 0;
- else
- err = -EINVAL;
-
- if (err < 0)
- goto failed;
-
- sink->c = c;
-
- return 1;
-
-proceed:
- if (!a2dp_get_config(sink->stream, rsp, fd)) {
- err = -EINVAL;
- goto failed;
- }
-
- return 0;
-
-failed:
- if (c)
- pending_connect_free(c);
- return -err;
-}
-
gboolean sink_is_active(struct device *dev)
{
struct sink *sink = dev->sink;
@@ -425,52 +294,6 @@ gboolean sink_is_active(struct device *dev)
return FALSE;
}
-void sink_set_state(struct device *dev, avdtp_state_t state)
-{
- struct sink *sink = dev->sink;
- int err = 0;
-
- if (sink->state == state)
- return;
-
- if (!sink->session || !sink->stream)
- goto failed;
-
- switch (sink->state) {
- case AVDTP_STATE_OPEN:
- if (state == AVDTP_STATE_STREAMING) {
- err = avdtp_start(sink->session, sink->stream);
- if (err == 0)
- return;
- }
- else if (state == AVDTP_STATE_IDLE) {
- err = avdtp_close(sink->session, sink->stream);
- if (err == 0)
- return;
- }
- break;
- case AVDTP_STATE_STREAMING:
- if (state == AVDTP_STATE_OPEN) {
- err = avdtp_suspend(sink->session, sink->stream);
- if (err == 0) {
- sink->suspending = TRUE;
- return;
- }
- }
- else if (state == AVDTP_STATE_IDLE) {
- err = avdtp_close(sink->session, sink->stream);
- if (err == 0)
- return;
- }
- break;
- default:
- goto failed;
- }
-
-failed:
- error("%s: Error changing states", dev->path);
-}
-
avdtp_state_t sink_get_state(struct device *dev)
{
struct sink *sink = dev->sink;
diff --git a/audio/sink.h b/audio/sink.h
index 7894a36c..309379b1 100644
--- a/audio/sink.h
+++ b/audio/sink.h
@@ -25,10 +25,7 @@
struct sink *sink_init(struct device *dev);
void sink_free(struct device *dev);
-int sink_get_config(struct device *dev, int sock, struct ipc_packet *req,
- int pkt_len, struct ipc_data_cfg **rsp, int *fd);
gboolean sink_is_active(struct device *dev);
-void sink_set_state(struct device *dev, avdtp_state_t state);
avdtp_state_t sink_get_state(struct device *dev);
gboolean sink_new_stream(struct device *dev, struct avdtp *session,
struct avdtp_stream *stream);
diff --git a/audio/unix.c b/audio/unix.c
index 52b27809..10856b93 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -43,21 +43,51 @@
#include "ipc.h"
#include "device.h"
#include "manager.h"
+#include "avdtp.h"
+#include "a2dp.h"
+#include "headset.h"
+#include "sink.h"
#include "unix.h"
+typedef enum {
+ TYPE_NONE,
+ TYPE_HEADSET,
+ TYPE_SINK,
+ TYPE_SOURCE
+} service_type_t;
+
+typedef void (*notify_cb_t) (struct device *dev, void *data);
+
struct unix_client {
struct device *dev;
+ service_type_t type;
+ union {
+ struct avdtp *session;
+ void *data;
+ } data;
int sock;
+ int req_id;
+ notify_cb_t disconnect;
+ notify_cb_t suspend;
+ notify_cb_t play;
};
static GSList *clients = NULL;
static int unix_sock = -1;
-static int unix_send_state(int sock, struct ipc_packet *pkt);
-
static void client_free(struct unix_client *client)
{
+ switch (client->type) {
+ case TYPE_SINK:
+ case TYPE_SOURCE:
+ if (client->data.session)
+ avdtp_unref(client->data.session);
+ break;
+ default:
+ break;
+ }
+
if (client->sock >= 0)
close(client->sock);
g_free(client);
@@ -96,12 +126,116 @@ static int unix_sendmsg_fd(int sock, int fd, struct ipc_packet *pkt)
return sendmsg(sock, &msgh, MSG_NOSIGNAL);
}
+static service_type_t select_service(struct device *dev)
+{
+ if (dev->sink && avdtp_is_connected(&dev->src, &dev->dst))
+ return TYPE_SINK;
+ else if (dev->headset && headset_is_active(dev))
+ return TYPE_HEADSET;
+ else if (dev->sink)
+ return TYPE_SINK;
+ else if (dev->headset)
+ return TYPE_HEADSET;
+ else
+ return TYPE_NONE;
+}
+
+static void a2dp_setup_complete(struct avdtp *session, struct device *dev,
+ struct avdtp_stream *stream,
+ void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[sizeof(struct ipc_data_cfg) + sizeof(struct ipc_codec_sbc)];
+ struct ipc_data_cfg *cfg = (void *) buf;
+ struct avdtp_service_capability *cap;
+ struct avdtp_media_codec_capability *codec_cap;
+ struct sbc_codec_cap *sbc_cap;
+ struct ipc_codec_sbc *sbc = (void *) cfg->data;
+ int fd;
+ GSList *caps;
+
+ if (!stream)
+ goto failed;
+
+ if (!avdtp_stream_get_transport(stream, &fd, &cfg->pkt_len, &caps)) {
+ error("Unable to get stream transport");
+ goto failed;
+ }
+
+ for (codec_cap = NULL; caps; caps = g_slist_next(caps)) {
+ cap = caps->data;
+ if (cap->category == AVDTP_MEDIA_CODEC) {
+ codec_cap = (void *) cap->data;
+ break;
+ }
+ }
+
+ if (codec_cap == NULL ||
+ codec_cap->media_codec_type != A2DP_CODEC_SBC) {
+ error("Unable to find matching codec capability");
+ goto failed;
+ }
+
+ cfg->fd_opt = CFG_FD_OPT_WRITE;
+
+ sbc_cap = (void *) codec_cap;
+ cfg->channels = sbc_cap->channel_mode == A2DP_CHANNEL_MODE_MONO ?
+ 1 : 2;
+ cfg->channel_mode = sbc_cap->channel_mode;
+ cfg->sample_size = 2;
+
+ switch (sbc_cap->frequency) {
+ case A2DP_SAMPLING_FREQ_16000:
+ cfg->rate = 16000;
+ break;
+ case A2DP_SAMPLING_FREQ_32000:
+ cfg->rate = 32000;
+ break;
+ case A2DP_SAMPLING_FREQ_44100:
+ cfg->rate = 44100;
+ break;
+ case A2DP_SAMPLING_FREQ_48000:
+ cfg->rate = 48000;
+ break;
+ }
+
+ cfg->codec = CFG_CODEC_SBC;
+ sbc->allocation = sbc_cap->allocation_method == A2DP_ALLOCATION_SNR ?
+ 0x01 : 0x00;
+ sbc->subbands = sbc_cap->subbands == A2DP_SUBBANDS_4 ? 4 : 8;
+
+ switch (sbc_cap->block_length) {
+ case A2DP_BLOCK_LENGTH_4:
+ sbc->blocks = 4;
+ break;
+ case A2DP_BLOCK_LENGTH_8:
+ sbc->blocks = 8;
+ break;
+ case A2DP_BLOCK_LENGTH_12:
+ sbc->blocks = 12;
+ break;
+ case A2DP_BLOCK_LENGTH_16:
+ sbc->blocks = 16;
+ break;
+ }
+
+ sbc->bitpool = sbc_cap->max_bitpool;
+
+ unix_send_cfg(client->sock, cfg, fd);
+
+ return;
+
+failed:
+ unix_send_cfg(client->sock, NULL, -1);
+ a2dp_source_unlock(dev, session);
+}
+
static void cfg_event(struct unix_client *client, struct ipc_packet *pkt,
int len)
{
struct ipc_data_cfg *rsp;
struct device *dev;
- int ret, fd;
+ int ret, fd, id;
dev = manager_get_connected_device();
if (dev)
@@ -112,19 +246,50 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt,
goto failed;
proceed:
- ret = device_get_config(dev, client->sock, pkt, len, &rsp, &fd);
- if (ret < 0)
+ client->type = select_service(dev);
+
+ switch (client->type) {
+ case TYPE_SINK:
+ if (!client->data.session)
+ client->data.session = avdtp_get(&dev->src, &dev->dst);
+
+ if (!a2dp_source_lock(dev, client->data.session)) {
+ error("Unable to lock A2DP source SEP");
+ goto failed;
+ }
+
+ id = a2dp_source_request_stream(client->data.session, dev,
+ TRUE, a2dp_setup_complete,
+ client);
+ if (id < 0) {
+ error("request_stream failed");
+ goto failed;
+ }
+
+ client->req_id = id;
+ client->disconnect = (notify_cb_t) a2dp_source_unlock;
+ client->suspend = (notify_cb_t) a2dp_source_suspend;
+ client->play = (notify_cb_t) a2dp_source_start_stream;
+
+ break;
+ case TYPE_HEADSET:
+ if (!headset_lock(dev, client->data.data)) {
+ error("Unable to lock headset");
+ goto failed;
+ }
+
+ ret = headset_get_config(dev, client->sock, pkt, len, &rsp,
+ &fd);
+ client->disconnect = (notify_cb_t) headset_unlock;
+ client->suspend = (notify_cb_t) headset_suspend;
+ client->play = (notify_cb_t) headset_play;
+ break;
+ default:
+ error("No known services for device");
goto failed;
+ }
client->dev = dev;
-
- /* Connecting in progress */
- if (ret == 1)
- return;
-
- unix_send_cfg(client->sock, rsp, fd);
- g_free(rsp);
-
return;
failed:
@@ -139,6 +304,7 @@ static void ctl_event(struct unix_client *client, struct ipc_packet *pkt,
static void state_event(struct unix_client *client, struct ipc_packet *pkt,
int len)
{
+#if 0
struct ipc_data_state *state = (struct ipc_data_state *) pkt->data;
struct device *dev = client->dev;
@@ -148,6 +314,7 @@ static void state_event(struct unix_client *client, struct ipc_packet *pkt,
state->state = device_get_state(dev);
unix_send_state(client->sock, pkt);
+#endif
}
static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
@@ -156,14 +323,30 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
struct ipc_packet *pkt = (void *) buf;
struct unix_client *client = data;
int len, len_check;
+ void *cb_data;
if (cond & G_IO_NVAL)
return FALSE;
+ switch (client->type) {
+ case TYPE_HEADSET:
+ cb_data = client->data.data;
+ break;
+ case TYPE_SINK:
+ case TYPE_SOURCE:
+ cb_data = client->data.session;
+ break;
+ default:
+ cb_data = NULL;
+ break;
+ }
+
if (cond & (G_IO_HUP | G_IO_ERR)) {
debug("Unix client disconnected");
- if (client->dev)
- device_set_state(client->dev, STATE_CONNECTED);
+ if (client->disconnect)
+ client->disconnect(client->dev, cb_data);
+ if (client->type == TYPE_SINK && client->req_id >= 0)
+ a2dp_source_cancel_stream(client->req_id);
goto failed;
}
@@ -339,6 +522,7 @@ int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd)
return 0;
}
+#if 0
static int unix_send_state(int sock, struct ipc_packet *pkt)
{
struct ipc_data_state *state = (struct ipc_data_state *) pkt->data;
@@ -359,3 +543,4 @@ static int unix_send_state(int sock, struct ipc_packet *pkt)
return 0;
}
+#endif