diff options
| author | Luiz Augusto von Dentz <luiz.dentz@openbossa.org> | 2007-12-03 22:41:29 +0000 | 
|---|---|---|
| committer | Luiz Augusto von Dentz <luiz.dentz@openbossa.org> | 2007-12-03 22:41:29 +0000 | 
| commit | 2934e194f3ffe754e18477113c870a7b98f88454 (patch) | |
| tree | 8af6231fad9f933710e04d0f5ca7434ccf236d70 /audio/a2dp.c | |
| parent | ae100c4c1395e1b306d2e91b5fed85f7b8d6dfbf (diff) | |
Handle new ipc messages properly and adapt the plugins.
Diffstat (limited to 'audio/a2dp.c')
| -rw-r--r-- | audio/a2dp.c | 712 | 
1 files changed, 310 insertions, 402 deletions
| diff --git a/audio/a2dp.c b/audio/a2dp.c index 83441617..bf644359 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -43,12 +43,10 @@  #include "sink.h"  #include "a2dp.h" -#define MAX_BITPOOL 64 -#define MIN_BITPOOL 2 -  /* The duration that streams without users are allowed to stay in   * STREAMING state. */  #define SUSPEND_TIMEOUT 5000 +#define RECONFIGURE_TIMEOUT 500  #ifndef MIN  # define MIN(x, y) ((x) < (y) ? (x) : (y)) @@ -69,20 +67,23 @@ struct a2dp_sep {  	gboolean starting;  }; -struct a2dp_stream_cb { -	a2dp_stream_cb_t cb; +struct a2dp_setup_cb { +	a2dp_config_cb_t config_cb; +	a2dp_stream_cb_t resume_cb; +	a2dp_stream_cb_t suspend_cb;  	void *user_data;  	int id;  }; -struct a2dp_stream_setup { +struct a2dp_setup {  	struct avdtp *session;  	struct a2dp_sep *sep;  	struct avdtp_stream *stream; -	struct avdtp_service_capability *media_codec; -	gboolean start; +	GSList *client_caps; +	gboolean reconfigure;  	gboolean canceled;  	GSList *cb; +	int ref;  };  static DBusConnection *connection = NULL; @@ -94,9 +95,20 @@ static uint32_t source_record_id = 0;  static uint32_t sink_record_id = 0;  static GSList *setups = NULL; +static unsigned int cb_id = 0; + +static struct a2dp_setup *setup_ref(struct a2dp_setup *setup) +{ +	setup->ref++; + +	debug("setup_ref(%p): ref=%d", setup, setup->ref); + +	return setup; +} -static void stream_setup_free(struct a2dp_stream_setup *s) +static void setup_free(struct a2dp_setup *s)  { +	debug("setup_free(%p)", s);  	setups = g_slist_remove(setups, s);  	if (s->session)  		avdtp_unref(s->session); @@ -105,6 +117,16 @@ static void stream_setup_free(struct a2dp_stream_setup *s)  	g_free(s);  } +static void setup_unref(struct a2dp_setup *setup) +{ +	setup->ref--; + +	debug("setup_unref(%p): ref=%d", setup, setup->ref); + +	if (setup->ref <= 0) +		setup_free(setup); +} +  static struct device *a2dp_get_dev(struct avdtp *session)  {  	bdaddr_t addr; @@ -114,35 +136,97 @@ static struct device *a2dp_get_dev(struct avdtp *session)  	return manager_device_connected(&addr, A2DP_SOURCE_UUID);  } -static gboolean finalize_stream_setup(struct a2dp_stream_setup *s, struct avdtp_error *err) +static gboolean finalize_config(struct a2dp_setup *s, struct avdtp_error *err)  {  	GSList *l; +	setup_ref(s);  	for (l = s->cb; l != NULL; l = l->next) { -		struct a2dp_stream_cb *cb = l->data; -		 -		cb->cb(s->session, s->sep, s->stream, cb->user_data, err); +		struct a2dp_setup_cb *cb = l->data; + +		if (cb->config_cb) { +			cb->config_cb(s->session, s->sep, s->stream, err, +					cb->user_data); +			cb->config_cb = NULL; +			setup_unref(s); +		}  	} -	stream_setup_free(s); +	setup_unref(s);  	return FALSE;  } -static gboolean finalize_stream_setup_errno(struct a2dp_stream_setup *s, int err) +static gboolean finalize_config_errno(struct a2dp_setup *s, int err)  {  	struct avdtp_error avdtp_err;  	avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err); -	return finalize_stream_setup(s, err ? &avdtp_err : NULL); +	return finalize_config(s, err ? &avdtp_err : NULL);  } -static struct a2dp_stream_setup *find_setup_by_session(struct avdtp *session) +static gboolean finalize_resume(struct a2dp_setup *s, struct avdtp_error *err) +{ +	GSList *l; + +	setup_ref(s); +	for (l = s->cb; l != NULL; l = l->next) { +		struct a2dp_setup_cb *cb = l->data; + +		if (cb->resume_cb) { +			cb->resume_cb(s->session, err, cb->user_data); +			cb->resume_cb = NULL; +			setup_unref(s); +		} +	} + +	setup_unref(s); +	return FALSE; +} + +static gboolean finalize_resume_errno(struct a2dp_setup *s, int err) +{ +	struct avdtp_error avdtp_err; + +	avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err); + +	return finalize_resume(s, err ? &avdtp_err : NULL); +} + +static gboolean finalize_suspend(struct a2dp_setup *s, struct avdtp_error *err) +{ +	GSList *l; + +	setup_ref(s); +	for (l = s->cb; l != NULL; l = l->next) { +		struct a2dp_setup_cb *cb = l->data; + +		if (cb->suspend_cb) { +			cb->suspend_cb(s->session, err, cb->user_data); +			cb->suspend_cb = NULL; +			setup_unref(s); +		} +	} + +	setup_unref(s); +	return FALSE; +} + +static gboolean finalize_suspend_errno(struct a2dp_setup *s, int err) +{ +	struct avdtp_error avdtp_err; + +	avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err); + +	return finalize_suspend(s, err ? &avdtp_err : NULL); +} + +static struct a2dp_setup *find_setup_by_session(struct avdtp *session)  {  	GSList *l;  	for (l = setups; l != NULL; l = l->next) { -		struct a2dp_stream_setup *setup = l->data; +		struct a2dp_setup *setup = l->data;  		if (setup->session == session)  			return setup; @@ -151,12 +235,12 @@ static struct a2dp_stream_setup *find_setup_by_session(struct avdtp *session)  	return NULL;  } -static struct a2dp_stream_setup *find_setup_by_dev(struct device *dev) +static struct a2dp_setup *find_setup_by_dev(struct device *dev)  {  	GSList *l;  	for (l = setups; l != NULL; l = l->next) { -		struct a2dp_stream_setup *setup = l->data; +		struct a2dp_setup *setup = l->data;  		struct device *setup_dev = a2dp_get_dev(setup->session);  		if (setup_dev == dev) @@ -186,197 +270,9 @@ static void stream_state_changed(struct avdtp_stream *stream,  		avdtp_unref(sep->session);  		sep->session = NULL;  	} - +	  	sep->stream = NULL; -} - -static uint8_t default_bitpool(uint8_t freq, uint8_t mode) -{ -	switch (freq) { -	case A2DP_SAMPLING_FREQ_16000: -	case A2DP_SAMPLING_FREQ_32000: -		return 53; -	case A2DP_SAMPLING_FREQ_44100: -		switch (mode) { -		case A2DP_CHANNEL_MODE_MONO: -		case A2DP_CHANNEL_MODE_DUAL_CHANNEL: -			return 31; -		case A2DP_CHANNEL_MODE_STEREO: -		case A2DP_CHANNEL_MODE_JOINT_STEREO: -			return 53; -		default: -			error("Invalid channel mode %u", mode); -			return 53; -		} -	case A2DP_SAMPLING_FREQ_48000: -		switch (mode) { -		case A2DP_CHANNEL_MODE_MONO: -		case A2DP_CHANNEL_MODE_DUAL_CHANNEL: -			return 29; -		case A2DP_CHANNEL_MODE_STEREO: -		case A2DP_CHANNEL_MODE_JOINT_STEREO: -			return 51; -		default: -			error("Invalid channel mode %u", mode); -			return 51; -		} -	default: -		error("Invalid sampling freq %u", freq); -		return 53; -	} -} - -static gboolean select_sbc_params(struct sbc_codec_cap *cap, -					struct sbc_codec_cap *supported) -{ -	uint max_bitpool, min_bitpool; - -	memset(cap, 0, sizeof(struct sbc_codec_cap)); - -	cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; -	cap->cap.media_codec_type = A2DP_CODEC_SBC; - -	if (supported->frequency & A2DP_SAMPLING_FREQ_44100) -		cap->frequency = A2DP_SAMPLING_FREQ_44100; -	else if (supported->frequency & A2DP_SAMPLING_FREQ_48000) -		cap->frequency = A2DP_SAMPLING_FREQ_48000; -	else if (supported->frequency & A2DP_SAMPLING_FREQ_32000) -		cap->frequency = A2DP_SAMPLING_FREQ_32000; -	else if (supported->frequency & A2DP_SAMPLING_FREQ_16000) -		cap->frequency = A2DP_SAMPLING_FREQ_16000; -	else { -		error("No supported frequencies"); -		return FALSE; -	} - -	if (supported->channel_mode & A2DP_CHANNEL_MODE_JOINT_STEREO) -		cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; -	else if (supported->channel_mode & A2DP_CHANNEL_MODE_STEREO) -		cap->channel_mode = A2DP_CHANNEL_MODE_STEREO; -	else if (supported->channel_mode & A2DP_CHANNEL_MODE_DUAL_CHANNEL) -		cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL; -	else if (supported->channel_mode & A2DP_CHANNEL_MODE_MONO) -		cap->channel_mode = A2DP_CHANNEL_MODE_MONO; -	else { -		error("No supported channel modes"); -		return FALSE; -	} - -	if (supported->block_length & A2DP_BLOCK_LENGTH_16) -		cap->block_length = A2DP_BLOCK_LENGTH_16; -	else if (supported->block_length & A2DP_BLOCK_LENGTH_12) -		cap->block_length = A2DP_BLOCK_LENGTH_12; -	else if (supported->block_length & A2DP_BLOCK_LENGTH_8) -		cap->block_length = A2DP_BLOCK_LENGTH_8; -	else if (supported->block_length & A2DP_BLOCK_LENGTH_4) -		cap->block_length = A2DP_BLOCK_LENGTH_4; -	else { -		error("No supported block lengths"); -		return FALSE; -	} - -	if (supported->subbands & A2DP_SUBBANDS_8) -		cap->subbands = A2DP_SUBBANDS_8; -	else if (supported->subbands & A2DP_SUBBANDS_4) -		cap->subbands = A2DP_SUBBANDS_4; -	else { -		error("No supported subbands"); -		return FALSE; -	} - -	if (supported->allocation_method & A2DP_ALLOCATION_LOUDNESS) -		cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; -	else if (supported->allocation_method & A2DP_ALLOCATION_SNR) -		cap->allocation_method = A2DP_ALLOCATION_SNR; - -	min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool); -	max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), -							supported->max_bitpool); - -	cap->min_bitpool = min_bitpool; -	cap->max_bitpool = max_bitpool; - -	return TRUE; -} -static gboolean a2dp_select_capabilities(struct avdtp *session, -						struct avdtp_remote_sep *rsep, -						GSList **caps) -{ -	struct avdtp_service_capability *media_transport, *media_codec; -	struct sbc_codec_cap sbc_cap; -	struct a2dp_stream_setup *setup; - -	setup = find_setup_by_session(session); -	if (!setup) -		return FALSE; - -	if (setup->media_codec) { -		memcpy(&sbc_cap, setup->media_codec->data, sizeof(sbc_cap)); -	} else { -		media_codec = avdtp_get_codec(rsep); -		if (!media_codec) -			return FALSE; - -		select_sbc_params(&sbc_cap, -			(struct sbc_codec_cap *) media_codec->data); -	} - -	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, -						NULL, 0); - -	*caps = g_slist_append(*caps, media_transport); - -	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, -						sizeof(sbc_cap)); - -	*caps = g_slist_append(*caps, media_codec); - - -	return TRUE; -} - -static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err, -				void *user_data) -{ -	struct avdtp_local_sep *lsep; -	struct avdtp_remote_sep *rsep; -	struct a2dp_stream_setup *setup; -	GSList *caps = NULL; -	int posix_err; - -	setup = find_setup_by_session(session); - -	if (!setup) -		return; - -	if (err || setup->canceled) { -		setup->stream = NULL; -		finalize_stream_setup(setup, err); -		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_errno(setup, -EINVAL); -		return; -	} - -	if (!a2dp_select_capabilities(session, rsep, &caps)) { -		error("Unable to select remote SEP capabilities"); -		finalize_stream_setup_errno(setup, -EINVAL); -		return; -	} - -	posix_err = avdtp_set_configuration(session, rsep, lsep, caps, -							&setup->stream); -	if (posix_err < 0) { -		error("avdtp_set_configuration: %s", strerror(-posix_err)); -		finalize_stream_setup_errno(setup, posix_err); -	}  }  static gboolean setconf_ind(struct avdtp *session, @@ -490,7 +386,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  				struct avdtp_error *err, void *user_data)  {  	struct a2dp_sep *a2dp_sep = user_data; -	struct a2dp_stream_setup *setup; +	struct a2dp_setup *setup;  	struct device *dev;  	int ret; @@ -503,7 +399,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  	if (err) {  		if (setup) -			finalize_stream_setup(setup, err); +			finalize_config(setup, err);  		return;  	} @@ -523,7 +419,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  		error("Error on avdtp_open %s (%d)", strerror(-ret),  				-ret);  		setup->stream = NULL; -		finalize_stream_setup_errno(setup, ret); +		finalize_config_errno(setup, ret);  	}  } @@ -569,8 +465,7 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  			void *user_data)  {  	struct a2dp_sep *a2dp_sep = user_data; -	struct a2dp_stream_setup *setup; -	int posix_err; +	struct a2dp_setup *setup;  	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)  		debug("SBC Sink: Open_Cfm"); @@ -584,28 +479,19 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  	if (setup->canceled) {  		if (!err)  			avdtp_close(session, stream); -		stream_setup_free(setup); +		setup_unref(setup);  		return;  	} +	if (setup->reconfigure) +		setup->reconfigure = FALSE; +  	if (err) {  		setup->stream = NULL; -		finalize_stream_setup(setup, err); -		return; -	} - -	if (setup->start) { -		posix_err = avdtp_start(session, stream);		 -		if (posix_err == 0) -			return; - -		error("avdtp_start failed"); -		setup->stream = NULL;		 +		finalize_config(setup, err);  	}  	else -		posix_err = 0; - -	finalize_stream_setup_errno(setup, -posix_err); +		finalize_config(setup, 0);  }  static gboolean suspend_timeout(struct a2dp_sep *sep) @@ -647,7 +533,7 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  			void *user_data)  {  	struct a2dp_sep *a2dp_sep = user_data; -	struct a2dp_stream_setup *setup; +	struct a2dp_setup *setup;  	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)  		debug("SBC Sink: Start_Cfm"); @@ -661,16 +547,16 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  	if (setup->canceled) {  		if (!err)  			avdtp_close(session, stream); -		stream_setup_free(setup); +		setup_unref(setup);  		return;  	}  	if (err) {  		setup->stream = NULL; -		finalize_stream_setup(setup, err); +		finalize_resume(setup, err);  	}  	else -		finalize_stream_setup_errno(setup, 0); +		finalize_resume_errno(setup, 0);  }  static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -699,8 +585,7 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  			void *user_data)  {  	struct a2dp_sep *a2dp_sep = user_data; -	struct a2dp_stream_setup *setup; -	int posix_err; +	struct a2dp_setup *setup;  	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)  		debug("SBC Sink: Suspend_Cfm"); @@ -714,15 +599,11 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  		return;  	if (err) { -		finalize_stream_setup(setup, err); -		return; -	} - -	if (setup->start) { -		posix_err = avdtp_start(session, stream); -		if (posix_err < 0) -			finalize_stream_setup_errno(setup, posix_err); +		setup->stream = NULL; +		finalize_suspend(setup, err);  	} +	else +		finalize_suspend_errno(setup, 0);  }  static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -739,13 +620,39 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,  	return TRUE;  } +static gboolean reconfigure(gpointer data) +{ +	struct a2dp_setup *setup = data; +	struct avdtp_local_sep *lsep; +	struct avdtp_remote_sep *rsep; +	int posix_err; + +	posix_err = avdtp_get_seps(setup->session, AVDTP_SEP_TYPE_SINK, +					AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC, +					&lsep, &rsep); +	if (posix_err < 0) { +		error("No matching ACP and INT SEPs found"); +		finalize_config_errno(setup, posix_err); +	} + +	posix_err = avdtp_set_configuration(setup->session, rsep, lsep, +						setup->client_caps, +						&setup->stream); +	if (posix_err < 0) { +		error("avdtp_set_configuration: %s", +			strerror(-posix_err)); +		finalize_config_errno(setup, posix_err); +	} + +	return FALSE; +} +  static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  			struct avdtp_stream *stream, struct avdtp_error *err,  			void *user_data)  {  	struct a2dp_sep *a2dp_sep = user_data; -	struct a2dp_stream_setup *setup; -	int posix_err; +	struct a2dp_setup *setup;  	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)  		debug("SBC Sink: Close_Cfm"); @@ -757,28 +664,18 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  		return;  	if (setup->canceled) { -		stream_setup_free(setup); +		setup_unref(setup);  		return;  	}  	if (err) {  		setup->stream = NULL; -		finalize_stream_setup(setup, err); +		finalize_config(setup, err);  		return;  	} -	if (setup->start) { -		posix_err = avdtp_discover(session, discovery_complete, setup); -		if (posix_err == 0) -			return; - -		error("avdtp_discover failed"); -		setup->stream = NULL; -	} -	else -		posix_err = 0; - -	finalize_stream_setup_errno(setup, -posix_err); +	if (setup->reconfigure) +		g_timeout_add(RECONFIGURE_TIMEOUT, reconfigure, setup);  }  static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -802,11 +699,18 @@ static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  			void *user_data)  {  	struct a2dp_sep *a2dp_sep = user_data; +	struct a2dp_setup *setup;  	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)  		debug("SBC Sink: Abort_Cfm");  	else  		debug("SBC Source: Abort_Cfm"); + +	setup = find_setup_by_session(session); +	if (!setup) +		return; + +	setup_unref(setup);  }  static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -826,8 +730,7 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  			void *user_data)  {  	struct a2dp_sep *a2dp_sep = user_data; -	struct a2dp_stream_setup *setup; -	int posix_err; +	struct a2dp_setup *setup;  	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)  		debug("SBC Sink: ReConfigure_Cfm"); @@ -841,28 +744,16 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  	if (setup->canceled) {  		if (!err)  			avdtp_close(session, stream); -		stream_setup_free(setup); +		setup_unref(setup);  		return;  	}  	if (err) {  		setup->stream = NULL; -		finalize_stream_setup(setup, err); -		return; -	} - -	if (setup->start) { -		posix_err = avdtp_start(session, stream);		 -		if (posix_err == 0) -			return; - -		error("avdtp_start failed"); -		setup->stream = NULL; +		finalize_config(setup, err);  	}  	else -		posix_err = 0; - -	finalize_stream_setup_errno(setup, -posix_err); +		finalize_config(setup, 0);  }  static struct avdtp_sep_cfm cfm = { @@ -1062,10 +953,10 @@ void a2dp_exit()  	dbus_connection_unref(connection);  } -gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id) +gboolean a2dp_source_cancel(struct device *dev, unsigned int id)  { -	struct a2dp_stream_cb *cb_data; -	struct a2dp_stream_setup *setup; +	struct a2dp_setup_cb *cb_data; +	struct a2dp_setup *setup;  	GSList *l;  	setup = find_setup_by_dev(dev); @@ -1073,7 +964,7 @@ gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id)  		return FALSE;  	for (cb_data = NULL, l = setup->cb; l != NULL; l = g_slist_next(l)) { -		struct a2dp_stream_cb *cb = l->data; +		struct a2dp_setup_cb *cb = l->data;  		if (cb->id == id) {  			cb_data = cb; @@ -1095,17 +986,16 @@ gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id)  	return TRUE;  } -unsigned int a2dp_source_request_stream(struct avdtp *session, -						gboolean start, -						a2dp_stream_cb_t cb, -						void *user_data, -						struct avdtp_service_capability *media_codec) +unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, +				GSList *caps, void *user_data)  { -	struct a2dp_stream_cb *cb_data; -	static unsigned int cb_id = 0; +	struct a2dp_setup_cb *cb_data;  	GSList *l; -	struct a2dp_stream_setup *setup; +	struct a2dp_setup *setup;  	struct a2dp_sep *sep = NULL; +	struct avdtp_local_sep *lsep; +	struct avdtp_remote_sep *rsep; +	int posix_err;  	for (l = sources; l != NULL; l = l->next) {  		struct a2dp_sep *tmp = l->data; @@ -1120,91 +1010,173 @@ unsigned int a2dp_source_request_stream(struct avdtp *session,  	}  	if (!sep) { -		error("a2dp_source_request_stream: no available SEP found"); +		error("a2dp_source_cfg: no available SEP found");  		return 0;  	} -	setup = find_setup_by_session(session); - -	debug("a2dp_source_request_stream: selected SEP %p", sep); +	debug("a2dp_source_config: selected SEP %p", sep); -	cb_data = g_new(struct a2dp_stream_cb, 1); -	cb_data->cb = cb; +	cb_data = g_new(struct a2dp_setup_cb, 1); +	cb_data->config_cb = cb;  	cb_data->user_data = user_data;  	cb_data->id = ++cb_id; -	if (setup) { -		setup->canceled = FALSE; -		setup->sep = sep; -		setup->cb = g_slist_append(setup->cb, cb_data); -		if (start) -			setup->start = TRUE; -		return cb_data->id; +	setup = find_setup_by_session(session); +	if (!setup) { +		setup = g_new0(struct a2dp_setup, 1); +		setup->session = avdtp_ref(session); +		setups = g_slist_append(setups, setup);  	} -	setup = g_new0(struct a2dp_stream_setup, 1); -	setup->session = avdtp_ref(session); -	setup->sep = sep; +	setup_ref(setup);  	setup->cb = g_slist_append(setup->cb, cb_data); -	setup->start = start; +	setup->sep = sep;  	setup->stream = sep->stream; -	setup->media_codec = media_codec; +	setup->client_caps = caps;  	switch (avdtp_sep_get_state(sep->sep)) {  	case AVDTP_STATE_IDLE: -		if (avdtp_discover(session, discovery_complete, setup) < 0) { -			error("avdtp_discover failed"); +		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"); +			goto failed; +		} + +		posix_err = avdtp_set_configuration(session, rsep, lsep, +							caps, &setup->stream); +		if (posix_err < 0) { +			error("avdtp_set_configuration: %s", +				strerror(-posix_err));  			goto failed;  		}  		break;  	case AVDTP_STATE_OPEN: -		if (!start) { -			g_idle_add((GSourceFunc) finalize_stream_setup, setup); -			break; -		} -		if (sep->starting) -			break; -		if (setup->media_codec) { -			if (avdtp_stream_has_capability(setup->stream, -							setup->media_codec)) { -				if (avdtp_start(session, sep->stream) < 0) { -					error("avdtp_start failed"); -					goto failed; -				} -			} else { -				if (avdtp_close(session, sep->stream) < 0) { -					error("avdtp_close failed"); -					goto failed; -				} +	case AVDTP_STATE_STREAMING: +		if (avdtp_stream_has_capabilities(setup->stream, caps)) +			g_idle_add((GSourceFunc) finalize_config, setup); +		else if (!setup->reconfigure) { +			setup->reconfigure = TRUE; +			if (avdtp_close(session, sep->stream) < 0) { +				error("avdtp_close failed"); +				goto failed;  			}  		} -		else if (avdtp_start(session, sep->stream) < 0) { +		break; +	default: +		error("SEP in bad state for requesting a new stream"); +		goto failed; +	} + +	return cb_data->id; + +failed: +	setup_unref(setup); +	cb_id--; +	return 0; +} + +unsigned int a2dp_source_resume(struct avdtp *session, struct a2dp_sep *sep, +				a2dp_stream_cb_t cb, void *user_data) +{ +	struct a2dp_setup_cb *cb_data; +	struct a2dp_setup *setup; + +	cb_data = g_new(struct a2dp_setup_cb, 1); +	cb_data->resume_cb = cb; +	cb_data->user_data = user_data; +	cb_data->id = ++cb_id; + +	setup = find_setup_by_session(session); +	if (!setup) { +		setup = g_new0(struct a2dp_setup, 1); +		setup->session = avdtp_ref(session); +		setups = g_slist_append(setups, setup); +	} + +	setup_ref(setup); +	setup->cb = g_slist_append(setup->cb, cb_data); +	setup->sep = sep; +	setup->stream = sep->stream; + +	switch (avdtp_sep_get_state(sep->sep)) { +	case AVDTP_STATE_IDLE: +		goto failed; +		break; +	case AVDTP_STATE_OPEN: +		if (avdtp_start(session, sep->stream) < 0) {  			error("avdtp_start failed");  			goto failed;  		}  		break;  	case AVDTP_STATE_STREAMING: -		if (!start || !sep->suspending) { -			if (sep->suspend_timer) { -				g_source_remove(sep->suspend_timer); -				sep->suspend_timer = 0; -				avdtp_unref(sep->session); -				sep->session = NULL; -			} -			g_idle_add((GSourceFunc) finalize_stream_setup, setup); +		if (!sep->suspending && sep->suspend_timer) { +			g_source_remove(sep->suspend_timer); +			sep->suspend_timer = 0; +			avdtp_unref(sep->session); +			sep->session = NULL;  		} +		g_idle_add((GSourceFunc) finalize_resume, setup);  		break;  	default: -		error("SEP in bad state for requesting a new stream"); +		error("SEP in bad state");  		goto failed;  	} -	setups = g_slist_append(setups, setup); +	return cb_data->id; + +failed: +	setup_unref(setup); +	cb_id--; +	return 0; +} + +unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep, +				a2dp_stream_cb_t cb, void *user_data) +{ +	struct a2dp_setup_cb *cb_data; +	struct a2dp_setup *setup; + +	cb_data = g_new(struct a2dp_setup_cb, 1); +	cb_data->suspend_cb = cb; +	cb_data->user_data = user_data; +	cb_data->id = ++cb_id; + +	setup = find_setup_by_session(session); +	if (!setup) { +		setup = g_new0(struct a2dp_setup, 1); +		setup->session = avdtp_ref(session); +		setups = g_slist_append(setups, setup); +	} + +	setup_ref(setup); +	setup->cb = g_slist_append(setup->cb, cb_data); +	setup->sep = sep; +	setup->stream = sep->stream; + +	switch (avdtp_sep_get_state(sep->sep)) { +	case AVDTP_STATE_IDLE: +		error("a2dp_source_suspend: no stream to suspend"); +		goto failed; +		break; +	case AVDTP_STATE_OPEN: +		g_idle_add((GSourceFunc) finalize_suspend, setup); +		break; +	case AVDTP_STATE_STREAMING: +		if (avdtp_start(session, sep->stream) < 0) { +			error("avdtp_start failed"); +			goto failed; +		} +		break; +	default: +		error("SEP in bad state for resume"); +		goto failed; +	}  	return cb_data->id;  failed: -	stream_setup_free(setup); +	setup_unref(setup);  	cb_id--;  	return 0;  } @@ -1248,67 +1220,3 @@ gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session)  	return TRUE;  } -gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session) -{ -	avdtp_state_t state; -	GSList *l; -	struct a2dp_sep *sep = NULL; - -	for (l = sources; l != NULL; l = l->next) { -		struct a2dp_sep *tmp = l->data; - -		if (tmp->session && tmp->session == session) { -			sep = tmp; -			break; -		} -	} - -	if (!sep) -		return FALSE; - -	state = avdtp_sep_get_state(sep->sep); - -	if (!sep->stream || state != AVDTP_STATE_STREAMING) -		return TRUE; - -	if (avdtp_suspend(session, sep->stream) == 0) { -		sep->suspending = TRUE; -		return TRUE; -	} - -	return FALSE; -} - -gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session) -{ -	avdtp_state_t state; -	GSList *l; -	struct a2dp_sep *sep = NULL; - -	for (l = sources; l != NULL; l = l->next) { -		struct a2dp_sep *tmp = l->data; - -		if (tmp->session && tmp->session == session) { -			sep = tmp; -			break; -		} -	} - -	if (!sep) -		return FALSE; - -	state = avdtp_sep_get_state(sep->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, sep->stream) < 0) -		return FALSE; - -	return TRUE; -} | 
