diff options
| -rw-r--r-- | audio/a2dp.c | 712 | ||||
| -rw-r--r-- | audio/a2dp.h | 26 | ||||
| -rw-r--r-- | audio/avdtp.c | 37 | ||||
| -rw-r--r-- | audio/avdtp.h | 2 | ||||
| -rw-r--r-- | audio/gsta2dpsink.c | 73 | ||||
| -rw-r--r-- | audio/ipc.h | 8 | ||||
| -rw-r--r-- | audio/pcm_bluetooth.c | 154 | ||||
| -rw-r--r-- | audio/sink.c | 212 | ||||
| -rw-r--r-- | audio/unix.c | 581 | 
9 files changed, 1225 insertions, 580 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; -} diff --git a/audio/a2dp.h b/audio/a2dp.h index 6579f64c..8227296f 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -48,6 +48,9 @@  #define A2DP_ALLOCATION_SNR		(1 << 1)  #define A2DP_ALLOCATION_LOUDNESS	1 +#define MAX_BITPOOL 64 +#define MIN_BITPOOL 2 +  #if __BYTE_ORDER == __LITTLE_ENDIAN  struct sbc_codec_cap { @@ -80,21 +83,24 @@ struct sbc_codec_cap {  struct a2dp_sep; -typedef void (*a2dp_stream_cb_t) (struct avdtp *session, struct a2dp_sep *sep, +typedef void (*a2dp_config_cb_t) (struct avdtp *session, struct a2dp_sep *sep,  					struct avdtp_stream *stream, -					void *user_data, -					struct avdtp_error *err); +					struct avdtp_error *err, +					void *user_data); +typedef void (*a2dp_stream_cb_t) (struct avdtp *session, +					struct avdtp_error *err, +					void *user_data);  int a2dp_init(DBusConnection *conn, int sources, int sinks);  void a2dp_exit(void); -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); -gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id); +unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, +				GSList *caps, void *user_data); +unsigned int a2dp_source_resume(struct avdtp *session, struct a2dp_sep *sep, +				a2dp_stream_cb_t cb, void *user_data); +unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep, +				a2dp_stream_cb_t cb, void *user_data); +gboolean a2dp_source_cancel(struct device *dev, unsigned int id);  gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session);  gboolean a2dp_sep_unlock(struct a2dp_sep *sep, 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 f68561e9..3acd4128 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1825,16 +1825,20 @@ static gboolean avdtp_discover_resp(struct avdtp *session,  				resp->seps[i].media_type, resp->seps[i].inuse);  		/* Skip SEP's which are in use */ +/*  		if (resp->seps[i].inuse)  			continue; +*/  		sep = find_remote_sep(session->seps, resp->seps[i].seid);  		if (!sep) {  			sep = g_new0(struct avdtp_remote_sep, 1);  			session->seps = g_slist_append(session->seps, sep);  		} +/*  		else if (sep && sep->stream)  			continue; +*/  		sep->seid = resp->seps[i].seid;  		sep->type = resp->seps[i].type; @@ -2275,6 +2279,21 @@ gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,  	return FALSE;  } +gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, +					GSList *caps) +{ +	GSList *l; + +	for (l = caps; l; l = g_slist_next(l)) { +		struct avdtp_service_capability *cap = l->data; + +		if (!avdtp_stream_has_capability(stream, cap)) +			return FALSE; +	} + +	return TRUE; +} +  gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,  					uint16_t *imtu, uint16_t *omtu,  					GSList **caps) @@ -2331,6 +2350,9 @@ struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,  {  	struct avdtp_service_capability *cap; +	if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_MEDIA_CODEC) +		return NULL; +  	cap = g_malloc(sizeof(struct avdtp_service_capability) + length);  	cap->category = category;  	cap->length = length; @@ -2457,6 +2479,18 @@ int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream)  	return send_request(session, FALSE, stream, &req, sizeof(req));  } +static void copy_capabilities(gpointer data, gpointer user_data) +{ +	struct avdtp_service_capability *src_cap = data; +	struct avdtp_service_capability *dst_cap; +	GSList **l = user_data; + +	dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data, +					src_cap->length); + +	*l = g_slist_append(*l, dst_cap); +} +  int avdtp_set_configuration(struct avdtp *session,  				struct avdtp_remote_sep *rsep,  				struct avdtp_local_sep *lsep, @@ -2484,7 +2518,8 @@ int avdtp_set_configuration(struct avdtp *session,  	new_stream->session = session;  	new_stream->lsep = lsep;  	new_stream->rseid = rsep->seid; -	new_stream->caps = caps; + +	g_slist_foreach(caps, copy_capabilities, &new_stream->caps);  	/* Calculate total size of request */  	for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) { diff --git a/audio/avdtp.h b/audio/avdtp.h index 88684f28..241fa713 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -221,6 +221,8 @@ gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,  gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,  				struct avdtp_service_capability *cap); +gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, +					GSList *caps);  int avdtp_set_configuration(struct avdtp *session,  				struct avdtp_remote_sep *rsep, diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index b24dc8b8..1b893cc1 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -45,6 +45,7 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug);  #define GST_CAT_DEFAULT a2dp_sink_debug  #define BUFFER_SIZE 2048 +#define TEMPLATE_MAX_BITPOOL_VALUE 64  #define GST_A2DP_SINK_MUTEX_LOCK(s) G_STMT_START {	\  		g_mutex_lock (s->sink_lock);		\ @@ -56,8 +57,8 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug);  struct bluetooth_data { -	struct bt_getcapabilities_rsp cfg; /* Bluetooth device config */ -	gint link_mtu; +	struct bt_getcapabilities_rsp caps; /* Bluetooth device capabilities */ +	struct bt_setconfiguration_rsp cfg; /* Bluetooth device configuration */  	int samples;			/* Number of encoded samples */  	gchar buffer[BUFFER_SIZE];	/* Codec transfer buffer */  	gsize count;			/* Codec transfer buffer counter */ @@ -67,6 +68,7 @@ struct bluetooth_data {  	int frame_count;		/* Current frames in buffer*/  }; +  #define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0)  #define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0) @@ -92,6 +94,7 @@ static GstStaticPadTemplate a2dp_sink_factory =  				"blocks = (int) { 4, 8, 12, 16 }, "  				"subbands = (int) { 4, 8 }, "  				"allocation = (string) { snr, loudness }," +				/* FIXME use constant here */  				"bitpool = (int) [ 2, 64 ]; "  				"audio/mpeg, "  				"mpegversion = (int) 1, " @@ -230,9 +233,10 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink,  					GstCaps *caps,  					sbc_capabilities_t *pkt)  { -	sbc_capabilities_t *cfg = &sink->data->cfg.sbc_capabilities; +	sbc_capabilities_t *cfg = &sink->data->caps.sbc_capabilities;  	const GValue *value = NULL;  	const char *pref, *name; +	gint rate, blocks, subbands;  	GstStructure *structure = gst_caps_get_structure(caps, 0);  	name = gst_structure_get_name(structure); @@ -243,7 +247,19 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink,  	}  	value = gst_structure_get_value(structure, "rate"); -	cfg->frequency = g_value_get_int(value); +	rate = g_value_get_int(value); +	if (rate == 44100) +		cfg->frequency = BT_A2DP_SAMPLING_FREQ_44100; +	else if (rate == 48000) +		cfg->frequency = BT_A2DP_SAMPLING_FREQ_48000; +	else if (rate == 32000) +		cfg->frequency = BT_A2DP_SAMPLING_FREQ_32000; +	else if (rate == 16000) +		cfg->frequency = BT_A2DP_SAMPLING_FREQ_16000; +	else { +		GST_ERROR_OBJECT(sink, "Invalid rate while setting caps"); +		return FALSE; +	}  	value = gst_structure_get_value(structure, "mode");  	pref = g_value_get_string(value); @@ -276,13 +292,34 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink,  	}  	value = gst_structure_get_value(structure, "subbands"); -	cfg->subbands = g_value_get_int(value); +	subbands = g_value_get_int(value); +	if (subbands == 8) +		cfg->subbands = BT_A2DP_SUBBANDS_8; +	else if (subbands == 4) +		cfg->subbands = BT_A2DP_SUBBANDS_4; +	else { +		GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands); +		return FALSE; +	}  	value = gst_structure_get_value(structure, "blocks"); -	cfg->block_length = g_value_get_int(value); +	blocks = g_value_get_int(value); +	if (blocks == 16) +		cfg->block_length = BT_A2DP_BLOCK_LENGTH_16; +	else if (blocks == 12) +		cfg->block_length = BT_A2DP_BLOCK_LENGTH_12; +	else if (blocks == 8) +		cfg->block_length = BT_A2DP_BLOCK_LENGTH_8; +	else if (blocks == 4) +		cfg->block_length = BT_A2DP_BLOCK_LENGTH_4; +	else { +		GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks); +		return FALSE; +	}  	/* FIXME min and max ??? */  	value = gst_structure_get_value(structure, "bitpool"); +  	cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value);  	memcpy(pkt, cfg, sizeof(*pkt)); @@ -375,7 +412,7 @@ static gboolean server_callback(GIOChannel *chan,  static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self)  { -	sbc_capabilities_t *sbc = &self->data->cfg.sbc_capabilities; +	sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities;  	GstStructure *structure;  	GValue *value;  	GValue *list; @@ -519,7 +556,9 @@ static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self)  	/* bitpool */  	value = g_value_init(value, GST_TYPE_INT_RANGE); -	gst_value_set_int_range(value, sbc->min_bitpool, sbc->max_bitpool); +	gst_value_set_int_range(value, +			MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL_VALUE), +			MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL_VALUE));  	gst_structure_set_value(structure, "bitpool", value);  	/* channels */ @@ -528,6 +567,8 @@ static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self)  	g_free(value); +	if (self->dev_caps != NULL) +		gst_caps_unref(self->dev_caps);  	self->dev_caps = gst_caps_new_full(structure, NULL);  	tmp = gst_caps_to_string(self->dev_caps); @@ -549,8 +590,6 @@ static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self)  	req->h.msg_type = BT_GETCAPABILITIES_REQ;  	strncpy(req->device, self->device, 18); -	req->transport = BT_CAPABILITIES_TRANSPORT_A2DP; -	req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;  	io_error = gst_a2dp_sink_audioservice_send(self, &req->h);  	if (io_error != G_IO_ERROR_NONE) {  		GST_ERROR_OBJECT(self, "Error while asking device caps"); @@ -570,12 +609,7 @@ static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self)  		return FALSE;  	} -	if (rsp->transport != BT_CAPABILITIES_TRANSPORT_A2DP) { -		GST_ERROR_OBJECT(self, "Non a2dp answer from device"); -		return FALSE; -	} - -	memcpy(&self->data->cfg, rsp, sizeof(*rsp)); +	memcpy(&self->data->caps, rsp, sizeof(*rsp));  	if (!gst_a2dp_sink_update_caps(self)) {  		GST_WARNING_OBJECT(self, "failed to update capabilities");  		return FALSE; @@ -685,6 +719,7 @@ static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps)  	memset (req, 0, sizeof(buf));  	req->h.msg_type = BT_SETCONFIGURATION_REQ; +	req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;  	strncpy(req->device, self->device, 18);  	ret = gst_a2dp_sink_init_pkt_conf(self, caps, &req->sbc_capabilities);  	if (!ret) { @@ -716,6 +751,7 @@ static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps)  		return FALSE;  	} +	memcpy(&self->data->cfg, rsp, sizeof(*rsp));  	GST_DEBUG_OBJECT(self, "configuration set");  	return TRUE; @@ -801,7 +837,10 @@ static GstCaps* gst_a2dp_sink_get_caps(GstBaseSink *basesink)  {  	GstA2dpSink *self = GST_A2DP_SINK(basesink); -	return self->dev_caps ? gst_caps_ref(self->dev_caps): NULL; +	if (self->dev_caps) +		return gst_caps_ref(self->dev_caps); + +	return gst_caps_copy(gst_pad_get_pad_template_caps(GST_BASE_SINK_PAD(self)));  }  static gboolean gst_a2dp_sink_set_caps(GstBaseSink *basesink, GstCaps *caps) diff --git a/audio/ipc.h b/audio/ipc.h index 0384cfd6..096c29f1 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -115,7 +115,6 @@ struct bt_getcapabilities_req {  	bt_audio_msg_header_t	h;  	char			device[18];	/* Address of the remote Device */  	uint8_t			transport;	/* Requested transport */ -	uint8_t			access_mode;	/* Requested access mode */  } __attribute__ ((packed));  /* BT_GETCAPABILITIES_RSP */ @@ -165,8 +164,6 @@ struct bt_getcapabilities_rsp {  	bt_audio_msg_header_t	h;  	uint8_t			posix_errno;  	uint8_t			transport;		/* Granted transport */ -	uint8_t			access_mode;		/* Granted access mode */ -	uint16_t		link_mtu;		/* Max length that transport supports */  	sbc_capabilities_t	sbc_capabilities;	/* A2DP only */  	mpeg_capabilities_t	mpeg_capabilities;	/* A2DP only */  	uint16_t		sampling_rate;		/* SCO only */ @@ -176,6 +173,8 @@ struct bt_getcapabilities_rsp {  struct bt_setconfiguration_req {  	bt_audio_msg_header_t	h;  	char			device[18];		/* Address of the remote Device */ +	uint8_t			transport;		/* Requested transport */ +	uint8_t			access_mode;		/* Requested access mode */  	sbc_capabilities_t	sbc_capabilities;	/* A2DP only - only one of this field  							and next one must be filled */  	mpeg_capabilities_t	mpeg_capabilities;	/* A2DP only */ @@ -184,6 +183,9 @@ struct bt_setconfiguration_req {  /* BT_SETCONFIGURATION_RSP */  struct bt_setconfiguration_rsp {  	bt_audio_msg_header_t	h; +	uint8_t			transport;		/* Granted transport */ +	uint8_t			access_mode;		/* Granted access mode */ +	uint16_t		link_mtu;		/* Max length that transport supports */  	uint8_t			posix_errno;  } __attribute__ ((packed)); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index a5bfcdf9..a177274a 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -69,6 +69,17 @@  #define SCO_RXBUFS 0x04  #endif +#ifndef MIN +# define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif + +#ifndef MAX +# define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + +#define MAX_BITPOOL 64 +#define MIN_BITPOOL 2 +  /* adapted from glibc sys/time.h timersub() macro */  #define priv_timespecsub(a, b, result)					\  	do {								\ @@ -411,6 +422,120 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io)  	return write(data->pipefd[1], &c, 1);  } +static uint8_t default_bitpool(uint8_t freq, uint8_t mode) +{ +	switch (freq) { +	case BT_A2DP_SAMPLING_FREQ_16000: +	case BT_A2DP_SAMPLING_FREQ_32000: +		return 53; +	case BT_A2DP_SAMPLING_FREQ_44100: +		switch (mode) { +		case BT_A2DP_CHANNEL_MODE_MONO: +		case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: +			return 31; +		case BT_A2DP_CHANNEL_MODE_STEREO: +		case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: +			return 53; +		default: +			DBG("Invalid channel mode %u", mode); +			return 53; +		} +	case BT_A2DP_SAMPLING_FREQ_48000: +		switch (mode) { +		case BT_A2DP_CHANNEL_MODE_MONO: +		case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: +			return 29; +		case BT_A2DP_CHANNEL_MODE_STEREO: +		case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: +			return 51; +		default: +			DBG("Invalid channel mode %u", mode); +			return 51; +		} +	default: +		DBG("Invalid sampling freq %u", freq); +		return 53; +	} +} + +static int select_sbc_params(sbc_capabilities_t *cap, unsigned int rate, +				unsigned int channels) +{ +	unsigned int max_bitpool, min_bitpool; + +	switch (rate) { +	case 48000: +		cap->frequency = BT_A2DP_SAMPLING_FREQ_48000; +		break; +	case 44100: +		cap->frequency = BT_A2DP_SAMPLING_FREQ_44100; +		break; +	case 32000: +		cap->frequency = BT_A2DP_SAMPLING_FREQ_32000; +		break; +	case 16000: +		cap->frequency = BT_A2DP_SAMPLING_FREQ_16000; +		break; +	default: +		DBG("Rate %d not supported", rate); +		return -1; +	} + +	if (channels == 2) { +		if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) +			cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; +		else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) +			cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; +		else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) +			cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; +	} else { +		if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) +			cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; +	} + +	if (!cap->channel_mode) { +		DBG("No supported channel modes"); +		return -1; +	} + +	if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16) +		cap->block_length = BT_A2DP_BLOCK_LENGTH_16; +	else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12) +		cap->block_length = BT_A2DP_BLOCK_LENGTH_12; +	else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8) +		cap->block_length = BT_A2DP_BLOCK_LENGTH_8; +	else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4) +		cap->block_length = BT_A2DP_BLOCK_LENGTH_4; +	else { +		DBG("No supported block lengths"); +		return -1; +	} + +	if (cap->subbands & BT_A2DP_SUBBANDS_8) +		cap->subbands = BT_A2DP_SUBBANDS_8; +	else if (cap->subbands & BT_A2DP_SUBBANDS_4) +		cap->subbands = BT_A2DP_SUBBANDS_4; +	else { +		DBG("No supported subbands"); +		return -1; +	} + +	if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) +		cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; +	else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR) +		cap->allocation_method = BT_A2DP_ALLOCATION_SNR; + +	min_bitpool = MAX(MIN_BITPOOL, cap->min_bitpool); +	max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), +							cap->max_bitpool); + +	cap->min_bitpool = min_bitpool; +	cap->max_bitpool = max_bitpool; + +	return 0; +} + +  static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,  					snd_pcm_hw_params_t *params)  { @@ -419,7 +544,8 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,  	char buf[BT_AUDIO_IPC_PACKET_SIZE];  	struct bt_setconfiguration_req *setconf_req = (void*) buf;  	struct bt_setconfiguration_rsp *setconf_rsp = (void*) buf; -	int err; +	unsigned int rate, channels; +	int err, dir;  	sbc_capabilities_t active_capabilities;  	DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", @@ -428,11 +554,20 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,  	/* FIXME: this needs to be really implemented (take into account  	real asoundrc settings + ALSA hw settings ) once server side sends us  	more than one possible configuration */ +	snd_pcm_hw_params_get_rate(params, &rate, &dir); +	snd_pcm_hw_params_get_channels(params, &channels); +	err = select_sbc_params(&a2dp->sbc_capabilities, rate, channels); +	if (err < 0) +		return err; +  	active_capabilities = a2dp->sbc_capabilities;  	memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE);  	setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ;  	setconf_req->sbc_capabilities = active_capabilities; +	setconf_req->access_mode = (io->stream == SND_PCM_STREAM_PLAYBACK ? +			BT_CAPABILITIES_ACCESS_MODE_WRITE : +			BT_CAPABILITIES_ACCESS_MODE_READ);  	err = audioservice_send(data->server.fd, &setconf_req->h);  	if (err < 0) @@ -450,13 +585,16 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,  		return -setconf_rsp->posix_errno;  	} +	data->transport = setconf_rsp->transport; +	data->link_mtu = setconf_rsp->link_mtu; +  	/* Setup SBC encoder now we agree on parameters */  	if (a2dp->sbc_initialized) -		sbc_finish(&a2dp->sbc); - -	/* FIXME: init using flags? */ -	sbc_init(&a2dp->sbc, 0); +		sbc_reinit(&a2dp->sbc, 0); +	else +		sbc_init(&a2dp->sbc, 0);  	a2dp->sbc_initialized = 1; +  	if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000)  		a2dp->sbc.rate = 16000; @@ -1327,11 +1465,11 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream,  		getcaps_req->transport = alsa_conf->transport;  	else  		getcaps_req->transport = BT_CAPABILITIES_TRANSPORT_ANY; - +/*  	getcaps_req->access_mode = (stream == SND_PCM_STREAM_PLAYBACK ?  			BT_CAPABILITIES_ACCESS_MODE_WRITE :  			BT_CAPABILITIES_ACCESS_MODE_READ); - +*/  	err = audioservice_send(data->server.fd, &getcaps_req->h);  	if (err < 0)  		goto failed; @@ -1348,7 +1486,9 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream,  	}  	data->transport = getcaps_rsp->transport; +/*  	data->link_mtu = getcaps_rsp->link_mtu; +*/  	if (getcaps_rsp->transport == BT_CAPABILITIES_TRANSPORT_A2DP)  		data->a2dp.sbc_capabilities = getcaps_rsp->sbc_capabilities; diff --git a/audio/sink.c b/audio/sink.c index b1fc5b2b..259abf8f 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -159,7 +159,7 @@ static gboolean stream_setup_retry(gpointer user_data)  static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,  					struct avdtp_stream *stream, -					void *user_data, struct avdtp_error *err) +					struct avdtp_error *err, void *user_data)  {  	struct sink *sink = user_data;  	struct pending_request *pending; @@ -190,23 +190,212 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,  	}  } +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) +{ +	unsigned int 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 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; + +	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 sink *sink = user_data; +	struct pending_request *pending; +	struct avdtp_local_sep *lsep; +	struct avdtp_remote_sep *rsep; +	GSList *caps = NULL; +	int id; + +	pending = sink->connect; + +	if (err) { +		avdtp_unref(sink->session); +		sink->session = NULL; +		if (avdtp_error_type(err) == AVDTP_ERROR_ERRNO +				&& avdtp_error_posix_errno(err) != EHOSTDOWN) { +			debug("connect:connect XCASE detected"); +			g_timeout_add(STREAM_SETUP_RETRY_TIMER, +					stream_setup_retry, sink); +		} else +			goto failed; +		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"); +		goto failed; +	} + +	if (!select_capabilities(session, rsep, &caps)) { +		error("Unable to select remote SEP capabilities"); +		goto failed; +	} + +	id = a2dp_source_config(sink->session, stream_setup_complete, +				caps, sink); +	if (id == 0) +		goto failed; + +	pending->id = id; +	return; + +failed: +	pending_request_free(pending); +	sink->connect = NULL; +	avdtp_unref(sink->session); +	sink->session = NULL; +	error_failed(pending->conn, pending->msg, "Stream setup failed"); +} +  static DBusHandlerResult sink_connect(DBusConnection *conn,  					DBusMessage *msg, void *data)  {  	struct device *dev = data;  	struct sink *sink = dev->sink;  	struct pending_request *pending; -	unsigned int id;  	if (!sink->session)  		sink->session = avdtp_get(&dev->src, &dev->dst);  	if (!sink->session) -		return error_failed(conn, msg, -						"Unable to get a session"); +		return error_failed(conn, msg, "Unable to get a session");  	if (sink->connect || sink->disconnect) -		return error_in_progress(conn, msg, "Device connection already in progress"); +		return error_in_progress(conn, msg, "Device connection" +					"already in progress");  	if (sink->state >= AVDTP_STATE_OPEN)  		return error_already_connected(conn, msg); @@ -216,21 +405,10 @@ static DBusHandlerResult sink_connect(DBusConnection *conn,  	pending->msg = dbus_message_ref(msg);  	sink->connect = pending; -	id = a2dp_source_request_stream(sink->session, FALSE, -					stream_setup_complete, sink, -					NULL); -	if (id == 0) { -		pending_request_free(pending); -		sink->connect = NULL; -		avdtp_unref(sink->session); -		sink->session = NULL; -		return error_failed(conn, msg, "Failed to request a stream"); -	} +	avdtp_discover(sink->session, discovery_complete, sink);  	debug("stream creation in progress"); -	pending->id = id; -  	return DBUS_HANDLER_RESULT_HANDLED;  } diff --git a/audio/unix.c b/audio/unix.c index ea96bd4f..c321b99b 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -71,7 +71,7 @@ struct headset_data {  struct unix_client {  	struct device *dev; -	struct avdtp_service_capability *media_codec; +	GSList *caps;  	service_type_t type;  	char *interface;  	union { @@ -83,18 +83,13 @@ struct unix_client {  	int data_fd; /* To be deleted once two phase configuration is fully implemented */  	unsigned int req_id;  	unsigned int cb_id; -	gboolean (*cancel_stream) (struct device *dev, unsigned int id); +	gboolean (*cancel) (struct device *dev, unsigned int id);  };  static GSList *clients = NULL;  static int unix_sock = -1; -static void unix_ipc_sendmsg(struct unix_client *client, -					const bt_audio_msg_header_t *msg); - -static void send_getcapabilities_rsp_error(struct unix_client *client, int err); -  static void client_free(struct unix_client *client)  {  	struct a2dp_data *a2dp; @@ -118,8 +113,10 @@ static void client_free(struct unix_client *client)  	if (client->sock >= 0)  		close(client->sock); -	if (client->media_codec) -		g_free(client->media_codec); +	if (client->caps) { +		g_slist_foreach(client->caps, (GFunc) g_free, NULL); +		g_slist_free(client->caps); +	}  	g_free(client->interface);  	g_free(client); @@ -152,6 +149,27 @@ static int unix_sendmsg_fd(int sock, int fd)  	return sendmsg(sock, &msgh, MSG_NOSIGNAL);  } +static void unix_ipc_sendmsg(struct unix_client *client, +					const bt_audio_msg_header_t *msg) +{ +	info("Audio API: sending %s", bt_audio_strmsg(msg->msg_type)); + +	if (send(client->sock, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) < 0) +		error("Error %s(%d)", strerror(errno), errno); +} + +static void unix_ipc_error(struct unix_client *client, int type, int err) +{ +	char buf[BT_AUDIO_IPC_PACKET_SIZE]; +	struct bt_getcapabilities_rsp *rsp = (void *) buf; + +	memset(buf, 0, sizeof(buf)); +	rsp->h.msg_type = type; +	rsp->posix_errno = err; + +	unix_ipc_sendmsg(client, &rsp->h); +} +  static service_type_t select_service(struct device *dev, const char *interface)  {  	if (!interface) { @@ -198,8 +216,8 @@ static void stream_state_changed(struct avdtp_stream *stream,  		break;  	}  } - -static void headset_setup_complete(struct device *dev, void *user_data) +/* +static void headset_discovery_complete(struct device *dev, void *user_data)  {  	struct unix_client *client = user_data;  	char buf[BT_AUDIO_IPC_PACKET_SIZE]; @@ -209,7 +227,7 @@ static void headset_setup_complete(struct device *dev, void *user_data)  	client->req_id = 0;  	if (!dev) { -		send_getcapabilities_rsp_error(client, EIO); +		unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO);  		client->dev = NULL;  		return;  	} @@ -231,7 +249,7 @@ static void headset_setup_complete(struct device *dev, void *user_data)  	if (!headset_lock(dev, hs->lock)) {  		error("Unable to lock headset"); -		send_getcapabilities_rsp_error(client, EIO); +		unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO);  		client->dev = NULL;  		return;  	} @@ -240,25 +258,131 @@ static void headset_setup_complete(struct device *dev, void *user_data)  	rsp->h.msg_type = BT_GETCAPABILITIES_RSP;  	rsp->transport  = BT_CAPABILITIES_TRANSPORT_SCO; -	rsp->access_mode = client->access_mode; -	rsp->link_mtu = 48;  	rsp->sampling_rate = 8000;  	client->data_fd = headset_get_sco_fd(dev);  	unix_ipc_sendmsg(client, &rsp->h);  } +*/ +static void headset_setup_complete(struct device *dev, void *user_data) +{ +	struct unix_client *client = user_data; +	char buf[BT_AUDIO_IPC_PACKET_SIZE]; +	struct bt_setconfiguration_rsp *rsp = (void *) buf; +	struct headset_data *hs = &client->d.hs; -static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, -					struct avdtp_stream *stream, -					void *user_data, struct avdtp_error *err) +	client->req_id = 0; + +	if (!dev) { +		unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); +		client->dev = NULL; +		return; +	} + +	switch (client->access_mode) { +	case BT_CAPABILITIES_ACCESS_MODE_READ: +		hs->lock = HEADSET_LOCK_READ; +		break; +	case BT_CAPABILITIES_ACCESS_MODE_WRITE: +		hs->lock = HEADSET_LOCK_WRITE; +		break; +	case BT_CAPABILITIES_ACCESS_MODE_READWRITE: +		hs->lock = HEADSET_LOCK_READ | HEADSET_LOCK_WRITE; +		break; +	default: +		hs->lock = 0; +		break; +	} + +	if (!headset_lock(dev, hs->lock)) { +		error("Unable to lock headset"); +		unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); +		client->dev = NULL; +		return; +	} + +	memset(buf, 0, sizeof(buf)); + +	rsp->h.msg_type = BT_SETCONFIGURATION_RSP; +	rsp->transport  = BT_CAPABILITIES_TRANSPORT_SCO; +	rsp->access_mode = client->access_mode; + +	client->data_fd = headset_get_sco_fd(dev); + +	unix_ipc_sendmsg(client, &rsp->h); +} + +static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, +					struct avdtp_error *err, +					void *user_data)  {  	struct unix_client *client = user_data;  	char buf[BT_AUDIO_IPC_PACKET_SIZE];  	struct bt_getcapabilities_rsp *rsp = (void *) buf; -	struct avdtp_service_capability *cap; -	struct avdtp_media_codec_capability *codec_cap; -	struct sbc_codec_cap *sbc_cap; +	struct a2dp_data *a2dp = &client->d.a2dp; +	struct sbc_codec_cap *sbc_cap = NULL; +	GSList *l; + +	if (err) +		goto failed; + +	memset(buf, 0, sizeof(buf)); +	client->req_id = 0; + +	rsp->h.msg_type = BT_GETCAPABILITIES_RSP; +	rsp->transport = BT_CAPABILITIES_TRANSPORT_A2DP; + +	for (l = seps; l; l = g_slist_next(l)) { +		struct avdtp_remote_sep *rsep = l->data; +		struct avdtp_service_capability *cap; +		struct avdtp_media_codec_capability *codec_cap; + +		cap = avdtp_get_codec(rsep); + +		if (cap->category != AVDTP_MEDIA_CODEC) +			continue; + +		codec_cap = (void *) cap->data; + +		if (codec_cap->media_codec_type == A2DP_CODEC_SBC && !sbc_cap) +			sbc_cap = (void *) codec_cap; + +	} + +	/* endianess prevent direct cast */ +	if (sbc_cap) { +		rsp->sbc_capabilities.channel_mode = sbc_cap->channel_mode; +		rsp->sbc_capabilities.frequency = sbc_cap->frequency; +		rsp->sbc_capabilities.allocation_method = sbc_cap->allocation_method; +		rsp->sbc_capabilities.subbands = sbc_cap->subbands; +		rsp->sbc_capabilities.block_length = sbc_cap->block_length; +		rsp->sbc_capabilities.min_bitpool = sbc_cap->min_bitpool; +		rsp->sbc_capabilities.max_bitpool = sbc_cap->max_bitpool; +	} + +	unix_ipc_sendmsg(client, &rsp->h); + +	return; + +failed: +	error("discovery failed"); +	unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); + +	avdtp_unref(a2dp->session); + +	a2dp->session = NULL; +	a2dp->stream = NULL; +} + +static void a2dp_config_complete(struct avdtp *session, struct a2dp_sep *sep, +					struct avdtp_stream *stream, +					struct avdtp_error *err, + 					void *user_data) +{ +	struct unix_client *client = user_data; +	char buf[BT_AUDIO_IPC_PACKET_SIZE]; +	struct bt_setconfiguration_rsp *rsp = (void *) buf;  	struct a2dp_data *a2dp = &client->d.a2dp;  	uint16_t imtu, omtu;  	GSList *caps; @@ -282,40 +406,13 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep,  		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; -	} - -	rsp->h.msg_type = BT_GETCAPABILITIES_RSP; +	rsp->h.msg_type = BT_SETCONFIGURATION_RSP;  	rsp->transport = BT_CAPABILITIES_TRANSPORT_A2DP;  	client->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;  	rsp->access_mode = client->access_mode;  	/* FIXME: Use imtu when fd_opt is CFG_FD_OPT_READ */  	rsp->link_mtu = omtu; -	sbc_cap = (void *) codec_cap; - -	/* assignations below are ok as soon as newipc.h and a2dp.h are kept */ -	/* in sync. However it is not possible to cast a struct to another   */ -	/* dues to endianess issues */ -	rsp->sbc_capabilities.channel_mode = sbc_cap->channel_mode; -	rsp->sbc_capabilities.frequency = sbc_cap->frequency; -	rsp->sbc_capabilities.allocation_method = sbc_cap->allocation_method; -	rsp->sbc_capabilities.subbands = sbc_cap->subbands; -	rsp->sbc_capabilities.block_length = sbc_cap->block_length; -	rsp->sbc_capabilities.min_bitpool = sbc_cap->min_bitpool; -	rsp->sbc_capabilities.max_bitpool = sbc_cap->max_bitpool; -  	unix_ipc_sendmsg(client, &rsp->h);  	client->cb_id = avdtp_stream_add_cb(session, stream, @@ -324,12 +421,86 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep,  	return;  failed: -	error("stream setup failed"); +	error("setup failed"); + +	if (a2dp->sep) { +		a2dp_sep_unlock(a2dp->sep, a2dp->session); +		a2dp->sep = NULL; +	} +	unix_ipc_error(client, BT_SETCONFIGURATION_RSP, EIO); + +	avdtp_unref(a2dp->session); + +	a2dp->session = NULL; +	a2dp->stream = NULL; +} + +static void a2dp_resume_complete(struct avdtp *session, +				struct avdtp_error *err, void *user_data) +{ +	struct unix_client *client = user_data; +	char buf[BT_AUDIO_IPC_PACKET_SIZE]; +	struct bt_streamstart_rsp *rsp = (void *) buf; +	struct bt_datafd_ind *ind = (void *) buf; +	struct a2dp_data *a2dp = &client->d.a2dp; + +	memset(buf, 0, sizeof(buf)); +	rsp->h.msg_type = BT_STREAMSTART_RSP; +	rsp->posix_errno = 0; +	unix_ipc_sendmsg(client, &rsp->h); + +	memset(buf, 0, sizeof(buf)); +	ind->h.msg_type = BT_STREAMFD_IND; +	unix_ipc_sendmsg(client, &ind->h); + +	if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) { +		error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno); +		goto failed; +	} + +	return; + +failed: +	error("resume failed"); + +	if (a2dp->sep) { +		a2dp_sep_unlock(a2dp->sep, a2dp->session); +		a2dp->sep = NULL; +	} +	unix_ipc_error(client, BT_STREAMSTART_REQ, EIO); + +	avdtp_unref(a2dp->session); + +	a2dp->session = NULL; +	a2dp->stream = NULL; +} + +static void a2dp_suspend_complete(struct avdtp *session, +				struct avdtp_error *err, void *user_data) +{ +	struct unix_client *client = user_data; +	char buf[BT_AUDIO_IPC_PACKET_SIZE]; +	struct bt_streamstart_rsp *rsp = (void *) buf; +	struct a2dp_data *a2dp = &client->d.a2dp; + +	if (err) +		goto failed; + +	memset(buf, 0, sizeof(buf)); +	rsp->h.msg_type = BT_STREAMSTOP_RSP; +	rsp->posix_errno = 0; +	unix_ipc_sendmsg(client, &rsp->h); + +	return; + +failed: +	error("suspend failed"); +  	if (a2dp->sep) {  		a2dp_sep_unlock(a2dp->sep, a2dp->session);  		a2dp->sep = NULL;  	} -	send_getcapabilities_rsp_error(client, EIO); +	unix_ipc_error(client, BT_STREAMSTOP_REQ, EIO);  	avdtp_unref(a2dp->session); @@ -337,10 +508,11 @@ failed:  	a2dp->stream = NULL;  } -static void create_stream(struct device *dev, struct unix_client *client) +static void start_discovery(struct device *dev, struct unix_client *client)  {  	struct a2dp_data *a2dp;  	unsigned int id; +	int err = 0;  	client->type = select_service(dev, client->interface); @@ -356,18 +528,55 @@ static void create_stream(struct device *dev, struct unix_client *client)  			goto failed;  		} -		/* FIXME: The provided media_codec breaks bitpool -                   selection. So disable it. This needs fixing */ -		id = a2dp_source_request_stream(a2dp->session, -						TRUE, a2dp_setup_complete, -						client, -						NULL/*client->media_codec*/); -		client->cancel_stream = a2dp_source_cancel_stream; +		err = avdtp_discover(a2dp->session, a2dp_discovery_complete, +					client); +		if (err) +			goto failed;  		break;  	case TYPE_HEADSET:  		id = headset_request_stream(dev, headset_setup_complete, client); -		client->cancel_stream = headset_cancel_stream; +		client->cancel = headset_cancel_stream; +		break; + +	default: +		error("No known services for device"); +		goto failed; +	} + +	return; + +failed: +	unix_ipc_error(client, BT_GETCAPABILITIES_RSP, err ? : EIO); +} + +static void start_config(struct device *dev, struct unix_client *client) +{ +	struct a2dp_data *a2dp; +	unsigned int id; + +	client->type = select_service(dev, client->interface); + +	switch (client->type) { +	case TYPE_SINK: +		a2dp = &client->d.a2dp; + +		if (!a2dp->session) +			a2dp->session = avdtp_get(&dev->src, &dev->dst); + +		if (!a2dp->session) { +			error("Unable to get a session"); +			goto failed; +		} + +		id = a2dp_source_config(a2dp->session, a2dp_config_complete, +					client->caps, client); +		client->cancel = a2dp_source_cancel; +		break; + +	case TYPE_HEADSET: +		id = headset_request_stream(dev, headset_setup_complete, client); +		client->cancel = headset_cancel_stream;  		break;  	default: @@ -376,7 +585,7 @@ static void create_stream(struct device *dev, struct unix_client *client)  	}  	if (id == 0) { -		error("request_stream failed"); +		error("config failed");  		goto failed;  	} @@ -386,37 +595,117 @@ static void create_stream(struct device *dev, struct unix_client *client)  	return;  failed: -	send_getcapabilities_rsp_error(client, EIO); +	unix_ipc_error(client, BT_SETCONFIGURATION_RSP, EIO);  } -static void create_cb(struct device *dev, void *user_data) +static void start_resume(struct device *dev, struct unix_client *client)  { -	struct unix_client *client = user_data; +	struct a2dp_data *a2dp; +	unsigned int id; -	if (!dev) -		send_getcapabilities_rsp_error(client, EIO); -	else -		create_stream(dev, client); +	client->type = select_service(dev, client->interface); + +	switch (client->type) { +	case TYPE_SINK: +		a2dp = &client->d.a2dp; + +		if (!a2dp->session) +			a2dp->session = avdtp_get(&dev->src, &dev->dst); + +		if (!a2dp->session) { +			error("Unable to get a session"); +			goto failed; +		} + +		if (!a2dp->sep) { +			error("Unable to get a sep"); +			goto failed; +		} + +		id = a2dp_source_resume(a2dp->session, a2dp->sep, +					a2dp_resume_complete, client); +		client->cancel = a2dp_source_cancel; +		break; + +	case TYPE_HEADSET: +		id = headset_request_stream(dev, headset_setup_complete, client); +		client->cancel = headset_cancel_stream; +		break; + +	default: +		error("No known services for device"); +		goto failed; +	} + +	if (id == 0) { +		error("resume failed"); +		goto failed; +	} + +	return; + +failed: +	unix_ipc_error(client, BT_STREAMSTART_RSP, EIO);  } -static void unix_ipc_sendmsg(struct unix_client *client, -					const bt_audio_msg_header_t *msg) +static void start_suspend(struct device *dev, struct unix_client *client)  { -	info("Audio API: sending %s", bt_audio_strmsg(msg->msg_type)); -	if (send(client->sock, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) < 0) -		error("Error %s(%d)", strerror(errno), errno); +	struct a2dp_data *a2dp; +	unsigned int id; + +	client->type = select_service(dev, client->interface); + +	switch (client->type) { +	case TYPE_SINK: +		a2dp = &client->d.a2dp; + +		if (!a2dp->session) +			a2dp->session = avdtp_get(&dev->src, &dev->dst); + +		if (!a2dp->session) { +			error("Unable to get a session"); +			goto failed; +		} + +		if (!a2dp->sep) { +			error("Unable to get a sep"); +			goto failed; +		} + +		id = a2dp_source_suspend(a2dp->session, a2dp->sep, +					a2dp_suspend_complete, client); +		client->cancel = a2dp_source_cancel; +		break; + +	case TYPE_HEADSET: +		id = headset_request_stream(dev, headset_setup_complete, client); +		client->cancel = headset_cancel_stream; +		break; + +	default: +		error("No known services for device"); +		goto failed; +	} + +	if (id == 0) { +		error("suspend failed"); +		goto failed; +	} + +	return; + +failed: +	unix_ipc_error(client, BT_STREAMSTOP_RSP, EIO);  } -static void send_getcapabilities_rsp_error(struct unix_client *client, int err) +static void create_cb(struct device *dev, void *user_data)  { -	char buf[BT_AUDIO_IPC_PACKET_SIZE]; -	struct bt_getcapabilities_rsp *rsp = (void *) buf; - -	memset(buf, 0, sizeof(buf)); -	rsp->h.msg_type = BT_GETCAPABILITIES_RSP; -	rsp->posix_errno = err; +	struct unix_client *client = user_data; -	unix_ipc_sendmsg(client, &rsp->h); +	if (!dev) +		unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); +	else +		start_discovery(dev, client);  }  static void handle_getcapabilities_req(struct unix_client *client, @@ -427,13 +716,6 @@ static void handle_getcapabilities_req(struct unix_client *client,  	str2ba(req->device, &bdaddr); -	if (!req->access_mode) { -		send_getcapabilities_rsp_error(client, EINVAL); -		return; -	} - -	client->access_mode = req->access_mode; -  	if (client->interface) {  		g_free(client->interface);  		client->interface = NULL; @@ -444,8 +726,6 @@ static void handle_getcapabilities_req(struct unix_client *client,  	else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP)  		client->interface = g_strdup(AUDIO_SINK_INTERFACE); -	client->media_codec = 0; -  	if (!manager_find_device(&bdaddr, NULL, FALSE)) {  		if (!bacmp(&bdaddr, BDADDR_ANY))  			goto failed; @@ -461,63 +741,118 @@ static void handle_getcapabilities_req(struct unix_client *client,  	if (!dev)  		goto failed; -	create_stream(dev, client); +	start_discovery(dev, client);  	return;  failed: -	send_getcapabilities_rsp_error(client, EIO); +	unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO);  }  static void handle_setconfiguration_req(struct unix_client *client,  					struct bt_setconfiguration_req *req)  { -	/* FIXME: for now we just blindly assume that we receive is the -	   only valid configuration sent.*/ -	char buf[BT_AUDIO_IPC_PACKET_SIZE]; -	struct bt_setconfiguration_rsp *rsp = (void *) buf; +	struct avdtp_service_capability *media_transport, *media_codec; +	struct sbc_codec_cap sbc_cap; +	struct device *dev; +	bdaddr_t bdaddr; +	int err = 0; -	memset(buf, 0, sizeof(buf)); -	rsp->h.msg_type = BT_SETCONFIGURATION_RSP; -	rsp->posix_errno = 0; +	if (!req->access_mode) { +		err = EINVAL; +		goto failed; +	} -	unix_ipc_sendmsg(client, &rsp->h); +	str2ba(req->device, &bdaddr); + +	if (client->interface) { +		g_free(client->interface); +		client->interface = NULL; +	} + +	if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO) +		client->interface = g_strdup(AUDIO_HEADSET_INTERFACE); +	else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP) +		client->interface = g_strdup(AUDIO_SINK_INTERFACE); + +	if (!manager_find_device(&bdaddr, NULL, FALSE)) { +		if (!bacmp(&bdaddr, BDADDR_ANY)) +			goto failed; +		if (!manager_create_device(&bdaddr, create_cb, client)) +			goto failed; +		return; +	} + +	dev = manager_find_device(&bdaddr, client->interface, TRUE); +	if (!dev) +		dev = manager_find_device(&bdaddr, client->interface, FALSE); + +	if (!dev) +		goto failed; + +	client->access_mode = req->access_mode; + +	if (client->caps) { +		g_slist_foreach(client->caps, (GFunc) g_free, NULL); +		g_slist_free(client->caps); +	} + +	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, +						NULL, 0); + +	client->caps = g_slist_append(client->caps, media_transport); + +	memset(&sbc_cap, 0, sizeof(sbc_cap)); + +	sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; +	sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC; +	sbc_cap.channel_mode = req->sbc_capabilities.channel_mode; +	sbc_cap.frequency = req->sbc_capabilities.frequency; +	sbc_cap.allocation_method = req->sbc_capabilities.allocation_method; +	sbc_cap.subbands = req->sbc_capabilities.subbands; +	sbc_cap.block_length = req->sbc_capabilities.block_length ; +	sbc_cap.min_bitpool = req->sbc_capabilities.min_bitpool; +	sbc_cap.max_bitpool = req->sbc_capabilities.max_bitpool; + +	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, +						sizeof(sbc_cap)); + +	client->caps = g_slist_append(client->caps, media_codec); + +	start_config(dev, client); + +	return; + +failed: +	unix_ipc_error(client, BT_SETCONFIGURATION_RSP, err ? : EIO);  }  static void handle_streamstart_req(struct unix_client *client,  					struct bt_streamstart_req *req)  { -	/* FIXME : to be really implemented */ -	char buf[BT_AUDIO_IPC_PACKET_SIZE]; -	struct bt_streamstart_rsp *rsp = (void *) buf; -	struct bt_datafd_ind *ind = (void *) buf; - -	memset(buf, 0, sizeof(buf)); -	rsp->h.msg_type = BT_STREAMSTART_RSP; -	rsp->posix_errno = 0; -	unix_ipc_sendmsg(client, &rsp->h); +	if (!client->dev) +		goto failed; -	memset(buf, 0, sizeof(buf)); -	ind->h.msg_type = BT_STREAMFD_IND; -	unix_ipc_sendmsg(client, &ind->h); +	start_resume(client->dev, client); -	if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) -		error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno); +	return; +failed: +	unix_ipc_error(client, BT_STREAMSTART_REQ, EIO);  }  static void handle_streamstop_req(struct unix_client *client,  					struct bt_streamstop_req *req)  { -	/* FIXME : to be implemented */ -	char buf[BT_AUDIO_IPC_PACKET_SIZE]; -	struct bt_streamstop_rsp *rsp = (void *) buf; +	if (!client->dev) +		goto failed; -	memset(buf, 0, sizeof(buf)); -	rsp->h.msg_type = BT_STREAMSTOP_RSP; -	rsp->posix_errno = 0; +	start_suspend(client->dev, client); -	unix_ipc_sendmsg(client, &rsp->h); +	return; + +failed: +	unix_ipc_error(client, BT_STREAMSTOP_REQ, EIO);  }  static void handle_control_req(struct unix_client *client, @@ -565,8 +900,8 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data)  			break;  		} -		if (client->cancel_stream && client->req_id > 0) -			client->cancel_stream(client->dev, client->req_id); +		if (client->cancel && client->req_id > 0) +			client->cancel(client->dev, client->req_id);  		goto failed;  	} | 
