diff options
| -rw-r--r-- | audio/a2dp.c | 147 | ||||
| -rw-r--r-- | audio/avdtp.c | 8 | 
2 files changed, 115 insertions, 40 deletions
diff --git a/audio/a2dp.c b/audio/a2dp.c index 2ad660b6..8206b880 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -41,6 +41,10 @@  #include "sink.h"  #include "a2dp.h" +/* The duration that streams without users are allowed to stay in + * STREAMING state. */ +#define SUSPEND_TIMEOUT 5000 +  #ifndef MIN  # define MIN(x, y) ((x) < (y) ? (x) : (y))  #endif @@ -51,9 +55,11 @@  struct a2dp_sep {  	struct avdtp_local_sep *sep; +	struct avdtp *session;  	struct avdtp_stream *stream;  	struct device *used_by;  	uint32_t record_id; +	guint suspend_timer;  	gboolean start_requested;  	gboolean suspending;  	gboolean starting; @@ -76,11 +82,26 @@ struct a2dp_stream_setup {  static DBusConnection *connection = NULL; -static struct a2dp_sep sink = { NULL, NULL, 0 }; -static struct a2dp_sep source = { NULL, NULL, 0 }; +static struct a2dp_sep sink = { NULL }; +static struct a2dp_sep source = { NULL };  static struct a2dp_stream_setup *setup = NULL; +static void stream_cleanup(struct a2dp_sep *sep) +{ +	if (sep->suspend_timer) { +		g_source_remove(sep->suspend_timer); +		sep->suspend_timer = 0; +	} + +	if (sep->session) { +		avdtp_unref(sep->session); +		sep->session = NULL; +	} + +	sep->stream = NULL; +} +  static void stream_setup_free(struct a2dp_stream_setup *s)  {  	if (s->session) @@ -109,16 +130,18 @@ static gboolean setconf_ind(struct avdtp *session,  				GSList *caps, uint8_t *err,  				uint8_t *category)  { +	struct a2dp_sep *a2dp_sep;  	struct device *dev;  	bdaddr_t addr;  	if (sep == sink.sep) {  		debug("SBC Sink: Set_Configuration_Ind"); -		return TRUE; +		a2dp_sep = &sink; +	} else { +		debug("SBC Source: Set_Configuration_Ind"); +		a2dp_sep = &source;  	} -	debug("SBC Source: Set_Configuration_Ind"); -  	avdtp_get_peers(session, NULL, &addr);  	dev = manager_device_connected(&addr, A2DP_SOURCE_UUID); @@ -128,9 +151,10 @@ static gboolean setconf_ind(struct avdtp *session,  		return FALSE;  	} -	source.stream = stream; +	a2dp_sep->stream = stream; -	sink_new_stream(dev, session, stream); +	if (a2dp_sep == &source) +		sink_new_stream(dev, session, stream);  	return TRUE;  } @@ -193,23 +217,25 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  				struct avdtp_stream *stream,  				struct avdtp_error *err)  { +	struct a2dp_sep *a2dp_sep;  	int ret;  	if (sep == sink.sep) {  		debug("SBC Sink: Set_Configuration_Cfm"); -		return; +		a2dp_sep = &sink; +	} else { +		debug("SBC Source: Set_Configuration_Cfm"); +		a2dp_sep = &source;  	} -	debug("SBC Source: Set_Configuration_Cfm"); -  	if (err) { -		source.stream = NULL; +		a2dp_sep->stream = NULL;  		if (setup)  			finalize_stream_setup(setup);  		return;  	} -	source.stream = stream; +	a2dp_sep->stream = stream;  	if (!setup)  		return; @@ -287,18 +313,40 @@ finalize:  	finalize_stream_setup(setup);  } +static gboolean suspend_timeout(struct a2dp_sep *sep) +{ +	if (avdtp_suspend(sep->session, sep->stream) == 0) +		sep->suspending = TRUE; + +	sep->suspend_timer = FALSE; + +	avdtp_unref(sep->session); +	sep->session = NULL; + +	return FALSE; +} +  static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep,  				struct avdtp_stream *stream, uint8_t *err)  { -	if (sep == sink.sep) +	struct a2dp_sep *a2dp_sep; + +	if (sep == sink.sep) {  		debug("SBC Sink: Start_Ind"); -	else +		a2dp_sep = &sink; +	} +	else {  		debug("SBC Source: Start_Ind"); +		a2dp_sep = &source; +	} -	/* Refuse to go into streaming state since this action should only be -	 * initiated by alsa */ -	*err = AVDTP_NOT_SUPPORTED_COMMAND; -	return FALSE; +	a2dp_sep->session = avdtp_ref(session); + +	a2dp_sep->suspend_timer = g_timeout_add(SUSPEND_TIMEOUT, +						(GSourceFunc) suspend_timeout, +						a2dp_sep); + +	return TRUE;  }  static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, @@ -338,39 +386,45 @@ 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, struct avdtp_error *err)  { +	struct a2dp_sep *a2dp_sep; +  	if (sep == sink.sep) {  		debug("SBC Sink: Suspend_Cfm"); -		return; +		a2dp_sep = &sink; +	} else { +		debug("SBC Source: Suspend_Cfm"); +		a2dp_sep = &source;  	} -	debug("SBC Source: Suspend_Cfm"); - -	source.suspending = FALSE; +	a2dp_sep->suspending = FALSE;  	if (err) { -		source.start_requested = FALSE; +		a2dp_sep->start_requested = FALSE;  		if (setup)  			finalize_stream_setup(setup);  		return;  	} -	if (source.start_requested) { +	if (a2dp_sep->start_requested) {  		avdtp_start(session, stream); -		source.start_requested = FALSE; +		a2dp_sep->start_requested = FALSE;  	}  }  static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,  				struct avdtp_stream *stream, uint8_t *err)  { +	struct a2dp_sep *a2dp_sep; +  	if (sep == sink.sep) {  		debug("SBC Sink: Close_Ind"); -		return TRUE; +		a2dp_sep = &sink; +	} else { +		debug("SBC Source: Close_Ind"); +		a2dp_sep = &source;  	} -	debug("SBC Source: Close_Ind"); - -	source.stream = NULL; +	stream_cleanup(a2dp_sep);  	return TRUE;  } @@ -378,27 +432,33 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,  static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  			struct avdtp_stream *stream, struct avdtp_error *err)  { +	struct a2dp_sep *a2dp_sep; +  	if (sep == sink.sep) {  		debug("SBC Sink: Close_Cfm"); -		return; +		a2dp_sep = &sink; +	} else { +		debug("SBC Source: Close_Cfm"); +		a2dp_sep = &source;  	} -	debug("SBC Source: Close_Cfm"); - -	source.stream = NULL; +	stream_cleanup(a2dp_sep);  }  static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,  				struct avdtp_stream *stream, uint8_t *err)  { +	struct a2dp_sep *a2dp_sep; +  	if (sep == sink.sep) {  		debug("SBC Sink: Abort_Ind"); -		return TRUE; +		a2dp_sep = &sink; +	} else { +		debug("SBC Source: Abort_Ind"); +		a2dp_sep = &source;  	} -	debug("SBC Source: Abort_Ind"); - -	source.stream = NULL; +	stream_cleanup(a2dp_sep);  	return TRUE;  } @@ -406,10 +466,17 @@ static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,  static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  			struct avdtp_stream *stream, struct avdtp_error *err)  { -	if (sep == sink.sep) +	struct a2dp_sep *a2dp_sep; + +	if (sep == sink.sep) {  		debug("SBC Sink: Abort_Cfm"); -	else +		a2dp_sep = &sink; +	} else {  		debug("SBC Source: Abort_Cfm"); +		a2dp_sep = &source; +	} + +	stream_cleanup(a2dp_sep);  }  static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -853,6 +920,8 @@ unsigned int a2dp_source_request_stream(struct avdtp *session,  		break;  	case AVDTP_STATE_STREAMING:  		if (!start || !source.suspending) { +			if (source.suspend_timer) +				g_source_remove(source.suspend_timer);  			g_idle_add((GSourceFunc) finalize_stream_setup, setup);  			return cb_data->id;  		} diff --git a/audio/avdtp.c b/audio/avdtp.c index 443f096b..8348d3d6 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -524,11 +524,17 @@ static void finalize_discovery(struct avdtp *session, int err)  static void release_stream(struct avdtp_stream *stream, struct avdtp *session)  { +	struct avdtp_local_sep *sep = stream->lsep; +  	if (stream->sock >= 0)  		close(stream->sock);  	if (stream->io)  		g_source_remove(stream->io); -	avdtp_sep_set_state(session, stream->lsep, AVDTP_STATE_IDLE); + +	if (sep->cfm && sep->cfm->abort) +		sep->cfm->abort(session, sep, stream, NULL); + +	avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);  }  static void connection_lost(struct avdtp *session, int err)  | 
