diff options
| -rw-r--r-- | audio/a2dp.c | 432 | ||||
| -rw-r--r-- | audio/a2dp.h | 4 | ||||
| -rw-r--r-- | audio/avdtp.c | 67 | ||||
| -rw-r--r-- | audio/avdtp.h | 6 | ||||
| -rw-r--r-- | audio/pcm_bluetooth.c | 116 | ||||
| -rw-r--r-- | audio/sink.c | 3 | ||||
| -rw-r--r-- | audio/unix.c | 129 | 
7 files changed, 545 insertions, 212 deletions
| diff --git a/audio/a2dp.c b/audio/a2dp.c index 5e08b25b..5ecd8159 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -76,6 +76,7 @@ struct a2dp_stream_setup {  	struct avdtp *session;  	struct device *dev;  	struct avdtp_stream *stream; +	struct avdtp_service_capability *media_codec;  	gboolean start;  	gboolean canceled;  	GSList *cb; @@ -138,6 +139,188 @@ static void stream_state_changed(struct avdtp_stream *stream,  	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_48000) +		cap->frequency = A2DP_SAMPLING_FREQ_48000; +	else if (supported->frequency & A2DP_SAMPLING_FREQ_44100) +		cap->frequency = A2DP_SAMPLING_FREQ_44100; +	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(2, 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_remote_sep *rsep, +						GSList **caps) +{ +	struct avdtp_service_capability *media_transport, *media_codec; +	struct sbc_codec_cap sbc_cap; + +	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, int err, +				void *user_data) +{ +	struct avdtp_local_sep *lsep; +	struct avdtp_remote_sep *rsep; +	GSList *caps = NULL; + +	if (err < 0) { +		setup->stream = NULL; +		finalize_stream_setup(setup); +		return; +	} + +	debug("Discovery complete"); + +	if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, +				A2DP_CODEC_SBC, &lsep, &rsep) < 0) { +		error("No matching ACP and INT SEPs found"); +		finalize_stream_setup(setup); +		return; +	} + +	if (!a2dp_select_capabilities(rsep, &caps)) { +		error("Unable to select remote SEP capabilities"); +		finalize_stream_setup(setup); +		return; +	} + +	err = avdtp_set_configuration(session, rsep, lsep, caps, +					&setup->stream); +	if (err < 0) { +		error("avdtp_set_configuration: %s", strerror(-err)); +		finalize_stream_setup(setup); +		return; +	} + +	/* Notify sink.c of the new stream */ +	sink_new_stream(setup->dev, session, setup->stream); +} +  static gboolean setconf_ind(struct avdtp *session,  				struct avdtp_local_sep *sep,  				struct avdtp_stream *stream, @@ -328,7 +511,7 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  		error("avdtp_start failed");  		setup->stream = NULL; -	}  +	}  finalize:  	finalize_stream_setup(setup); @@ -456,6 +639,30 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  		debug("SBC Sink: Close_Cfm");  	else  		debug("SBC Source: Close_Cfm"); + +	if (!setup) +		return; + +	if (setup->canceled) { +		stream_setup_free(setup); +		return; +	} + +	if (err) { +		setup->stream = NULL; +		goto finalize; +	} + +	if (setup->start) { +		if (avdtp_discover(session, discovery_complete, setup) == 0) +			return; + +		error("avdtp_discover failed"); +		setup->stream = NULL; +	} + +finalize: +	finalize_stream_setup(setup);  }  static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -506,6 +713,32 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,  		debug("SBC Sink: ReConfigure_Cfm");  	else  		debug("SBC Source: ReConfigure_Cfm"); + +	if (!setup) +		return; + +	if (setup->canceled) { +		if (!err) +			avdtp_close(session, stream); +		stream_setup_free(setup); +		return; +	} + +	if (err) { +		setup->stream = NULL; +		goto finalize; +	} + +	if (setup->start) { +		if (avdtp_start(session, stream) == 0) +			return; + +		error("avdtp_start failed"); +		setup->stream = NULL; +	} + +finalize: +	finalize_stream_setup(setup);  }  static struct avdtp_sep_cfm cfm = { @@ -705,181 +938,6 @@ void a2dp_exit()  	dbus_connection_unref(connection);  } -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_48000) -		cap->frequency = A2DP_SAMPLING_FREQ_48000; -	else if (supported->frequency & A2DP_SAMPLING_FREQ_44100) -		cap->frequency = A2DP_SAMPLING_FREQ_44100; -	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(2, 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_remote_sep *rsep, GSList **caps) -{ -	struct avdtp_service_capability *media_transport, *media_codec; -	struct sbc_codec_cap sbc_cap, *acp_sbc; - -	media_codec = avdtp_get_codec(rsep); -	if (!media_codec) -		return FALSE; - -	acp_sbc = (void *) media_codec->data; - -	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, -						NULL, 0); - -	*caps = g_slist_append(*caps, media_transport); - -	select_sbc_params(&sbc_cap, acp_sbc); - -	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, int err, -				void *user_data) -{ -	struct avdtp_local_sep *lsep; -	struct avdtp_remote_sep *rsep; -	GSList *caps = NULL; - -	if (err < 0) { -		error("Discovery failed: %s (%d)", strerror(-err), -err); -		setup->stream = NULL; -		finalize_stream_setup(setup); -		return; -	} - -	debug("Discovery complete"); - -	if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, -				A2DP_CODEC_SBC, &lsep, &rsep) < 0) { -		error("No matching ACP and INT SEPs found"); -		finalize_stream_setup(setup); -		return; -	} - -	if (!a2dp_select_capabilities(rsep, &caps)) { -		error("Unable to select remote SEP capabilities"); -		finalize_stream_setup(setup); -		return; -	}                        - -	err = avdtp_set_configuration(session, rsep, lsep, caps, -					&setup->stream); -	if (err < 0) { -		error("avdtp_set_configuration: %s", strerror(-err)); -		finalize_stream_setup(setup); -		return; -	} - -	/* Notify sink.c of the new stream */ -	sink_new_stream(setup->dev, session, setup->stream); -} -  gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id)  {  	struct a2dp_stream_cb *cb_data; @@ -914,7 +972,8 @@ unsigned int a2dp_source_request_stream(struct avdtp *session,  						gboolean start,  						a2dp_stream_cb_t cb,  						void *user_data, -						struct a2dp_sep **ret) +						struct a2dp_sep **ret, +						struct avdtp_service_capability *media_codec)  {  	struct a2dp_stream_cb *cb_data;  	static unsigned int cb_id = 0; @@ -967,6 +1026,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session,  	setup->cb = g_slist_append(setup->cb, cb_data);  	setup->start = start;  	setup->stream = sep->stream; +	setup->media_codec = media_codec;  	switch (avdtp_sep_get_state(sep->sep)) {  	case AVDTP_STATE_IDLE: @@ -982,7 +1042,21 @@ unsigned int a2dp_source_request_stream(struct avdtp *session,  		}  		if (sep->starting)  			break; -		if (avdtp_start(session, sep->stream) < 0) { +		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; +				} +			} +		} +		else if (avdtp_start(session, sep->stream) < 0) {  			error("avdtp_start failed");  			goto failed;  		} @@ -1006,7 +1080,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session,  		error("SEP in bad state for requesting a new stream");  		goto failed;  	} -	 +  	if (ret)  		*ret = sep; diff --git a/audio/a2dp.h b/audio/a2dp.h index 7358473e..ede0c70b 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -71,11 +71,11 @@ unsigned int a2dp_source_request_stream(struct avdtp *session,  					struct device *dev,  					gboolean start, a2dp_stream_cb_t cb,  					void *user_data, -					struct a2dp_sep **sep); +					struct a2dp_sep **sep, +					struct avdtp_service_capability *media_codec);  gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id);  gboolean a2dp_source_lock(struct device *dev, struct avdtp *session);  gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session);  gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session);  gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session); - diff --git a/audio/avdtp.c b/audio/avdtp.c index a967be76..d03afbae 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -552,9 +552,6 @@ static void stream_free(struct avdtp_stream *stream)  	if (stream->timer)  		g_source_remove(stream->timer); -	if (stream->io) -		g_source_remove(stream->io); -  	g_slist_foreach(stream->callbacks, (GFunc) g_free, NULL);  	g_slist_free(stream->callbacks); @@ -718,7 +715,7 @@ void avdtp_unref(struct avdtp *session)  			session->sock = -1;  		} -	       	if (session->sock >= 0) +		if (session->sock >= 0)  			set_disconnect_timer(session);  		else if (!session->free_lock) /* Drop the local ref if we  						 aren't connected */ @@ -954,7 +951,7 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session,  		err = AVDTP_SEP_IN_USE;  		goto failed;  	} -	 +  	stream = g_new0(struct avdtp_stream, 1);  	stream->session = session;  	stream->lsep = sep; @@ -1074,7 +1071,7 @@ static gboolean avdtp_start_cmd(struct avdtp *session, struct start_req *req,  		error("Too short start request");  		return FALSE;  	} -	 +  	seid_count = 1 + size - sizeof(struct start_req);  	seid = &req->first_seid; @@ -1936,8 +1933,8 @@ static gboolean avdtp_abort_resp(struct avdtp *session,  {  	struct avdtp_local_sep *sep = stream->lsep; -	if (sep->cfm && sep->cfm->suspend) -		sep->cfm->suspend(session, sep, stream, NULL, sep->user_data); +	if (sep->cfm && sep->cfm->abort) +		sep->cfm->abort(session, sep, stream, NULL, sep->user_data);  	avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); @@ -2196,7 +2193,7 @@ struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst)  gboolean avdtp_is_connected(bdaddr_t *src, bdaddr_t *dst)  {  	struct avdtp *session; -	 +  	session = find_session(src, dst);  	if (!session) @@ -2208,6 +2205,24 @@ gboolean avdtp_is_connected(bdaddr_t *src, bdaddr_t *dst)  	return FALSE;  } +gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, +				struct avdtp_service_capability *cap) +{ +	GSList *l; +	struct avdtp_service_capability *stream_cap; + +	for (l = stream->caps; l; l = g_slist_next(l)) { +		stream_cap = l->data; +		if (stream_cap->category == cap->category && +			stream_cap->length == cap->length) { +			if (!memcmp(stream_cap->data, cap->data, cap->length)) +				return TRUE; +		} +	} + +	return FALSE; +} +  gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,  					uint16_t *mtu, GSList **caps)  { @@ -2219,7 +2234,7 @@ gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,  	if (mtu)  		*mtu = stream->mtu; -	 +  	if (caps)  		*caps = stream->caps; @@ -2452,9 +2467,14 @@ int avdtp_set_configuration(struct avdtp *session,  	return ret;  } -int avdtp_reconfigure(struct avdtp *session, struct avdtp_stream *stream) +int avdtp_reconfigure(struct avdtp *session, GSList *caps, +			struct avdtp_stream *stream)  { -	struct seid_req req; +	struct reconf_req *req; +	unsigned char *ptr; +	int caps_len; +	GSList *l; +	struct avdtp_service_capability *cap;  	if (!g_slist_find(session->streams, stream))  		return -EINVAL; @@ -2462,11 +2482,26 @@ int avdtp_reconfigure(struct avdtp *session, struct avdtp_stream *stream)  	if (stream->lsep->state != AVDTP_STATE_OPEN)  		return -EINVAL; -	memset(&req, 0, sizeof(req)); -	init_request(&req.header, AVDTP_GET_CONFIGURATION); -	req.acp_seid = stream->rseid; +	/* Calculate total size of request */ +	for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) { +		cap = l->data; +		caps_len += cap->length + 2; +	} + +	req = g_malloc0(sizeof(struct reconf_req) + caps_len); + +	init_request(&req->header, AVDTP_RECONFIGURE); +	req->acp_seid = stream->rseid; + +	/* Copy the capabilities into the request */ +	for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) { +		cap = l->data; +		memcpy(ptr, cap, cap->length + 2); +		ptr += cap->length + 2; +	} -	return send_request(session, FALSE, NULL, &req, sizeof(req)); +	return send_request(session, FALSE, stream, req, sizeof(*req) +				+ caps_len);  }  int avdtp_open(struct avdtp *session, struct avdtp_stream *stream) diff --git a/audio/avdtp.h b/audio/avdtp.h index d759be48..d429b435 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -189,6 +189,9 @@ gboolean avdtp_stream_remove_cb(struct avdtp *session,  gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,  					uint16_t *mtu, GSList **caps); +gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, +				struct avdtp_service_capability *cap); +  int avdtp_set_configuration(struct avdtp *session,  				struct avdtp_remote_sep *rsep,  				struct avdtp_local_sep *lsep, @@ -199,7 +202,8 @@ int avdtp_get_configuration(struct avdtp *session,  				struct avdtp_stream *stream);  int avdtp_open(struct avdtp *session, struct avdtp_stream *stream); -int avdtp_reconfigure(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_reconfigure(struct avdtp *session, GSList *caps, +			struct avdtp_stream *stream);  int avdtp_start(struct avdtp *session, struct avdtp_stream *stream);  int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream);  int avdtp_close(struct avdtp *session, struct avdtp_stream *stream); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index d031b318..c6dc0931 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -893,19 +893,13 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data,  	return 0;  } -static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf) +static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf)  { -	int ret, total; -	char buf[IPC_MTU]; -	struct ipc_packet *pkt = (void *) buf;  	struct ipc_data_cfg *cfg = (void *) pkt->data;  	struct ipc_codec_sbc *sbc = (void *) cfg->data;  	snd_config_iterator_t i, next; -	const char *addr, *pref; - -	DBG("Sending PKT_TYPE_CFG_REQ..."); - -	memset(buf, 0, sizeof(buf)); +	const char *addr, *pref, *mode, *allocation, *rate, *channels, +		*subbands, *blocks, *bitpool;  	snd_config_for_each(i, next, conf) {  		snd_config_t *n = snd_config_iterator_entry(i); @@ -944,14 +938,114 @@ static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf)  			continue;  		} +		if (strcmp(id, "rate") == 0) { +			if (snd_config_get_string(n, &rate) < 0) { +				SNDERR("Invalid type for %s", id); +				return -EINVAL; +			} + +			cfg->rate = strtod(rate, NULL); +			continue; +		} + +		if (strcmp(id, "channels") == 0) { +			if (snd_config_get_string(n, &channels) < 0) { +				SNDERR("Invalid type for %s", id); +				return -EINVAL; +			} + +			cfg->channels = strtod(channels, NULL); +			continue; +		} + +		if (strcmp(id, "channel_mode") == 0) { +			if (snd_config_get_string(n, &mode) < 0) { +				SNDERR("Invalid type for %s", id); +				return -EINVAL; +			} + +			if (strcmp(pref, "mono") == 0) +				cfg->channel_mode = CFG_CHANNEL_MODE_MONO; +			else if (strcmp(pref, "dual") == 0) +				cfg->channel_mode = CFG_CHANNEL_MODE_DUAL_CHANNEL; +			else if (strcmp(pref, "stereo") == 0) +				cfg->channel_mode = CFG_CHANNEL_MODE_STEREO; +			else if (strcmp(pref, "joint") == 0) +				cfg->channel_mode = CFG_CHANNEL_MODE_JOINT_STEREO; +			continue; +		} + +		if (strcmp(id, "allocation") == 0) { +			if (snd_config_get_string(n, &allocation) < 0) { +				SNDERR("Invalid type for %s", id); +				return -EINVAL; +			} + +			if (strcmp(pref, "snr") == 0) +				sbc->allocation = CODEC_SBC_ALLOCATION_SNR; +			else if (strcmp(pref, "loudness") == 0) +				sbc->allocation = CODEC_SBC_ALLOCATION_LOUDNESS; +			continue; +		} + +		if (strcmp(id, "subbands") == 0) { +			if (snd_config_get_string(n, &subbands) < 0) { +				SNDERR("Invalid type for %s", id); +				return -EINVAL; +			} + +			sbc->subbands = strtod(subbands, NULL); +			continue; +		} + +		if (strcmp(id, "blocks") == 0) { +			if (snd_config_get_string(n, &blocks) < 0) { +				SNDERR("Invalid type for %s", id); +				return -EINVAL; +			} + +			sbc->blocks = strtod(blocks, NULL); +			continue; +		} + +		if (strcmp(id, "bitpool") == 0) { +			if (snd_config_get_string(n, &bitpool) < 0) { +				SNDERR("Invalid type for %s", id); +				return -EINVAL; +			} + +			sbc->bitpool = strtod(bitpool, NULL); +			continue; +		} +  		SNDERR("Unknown field %s", id);  		return -EINVAL;  	} +	pkt->length = sizeof(*cfg) + sizeof(*sbc);  	pkt->type = PKT_TYPE_CFG_REQ;  	pkt->error = PKT_ERROR_NONE; -	ret = send(data->sock, pkt, sizeof(struct ipc_packet), 0); +	return 0; +} + +static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf) +{ +	int ret, total; +	char buf[IPC_MTU]; +	struct ipc_packet *pkt = (void *) buf; +	struct ipc_data_cfg *cfg = (void *) pkt->data; +	struct ipc_codec_sbc *sbc = (void *) cfg->data; + +	DBG("Sending PKT_TYPE_CFG_REQ..."); + +	memset(buf, 0, sizeof(buf)); + +	ret = bluetooth_cfg_init(pkt, conf); +	if (ret < 0) +		return -ret; + +	ret = send(data->sock, pkt, sizeof(*pkt) + pkt->length, 0);  	if (ret < 0)  		return -errno;  	else if (ret == 0) @@ -1026,7 +1120,7 @@ done:  	while (recv(data->stream_fd, data->buffer, data->cfg.pkt_len,  				MSG_DONTWAIT) > 0); -	memset(data->buffer, 0, data->cfg.pkt_len); +	memset(data->buffer, 0, sizeof(data->buffer));  	return 0;  } diff --git a/audio/sink.c b/audio/sink.c index 5b699fb9..599fdaaf 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -188,7 +188,8 @@ static DBusHandlerResult sink_connect(DBusConnection *conn,  	sink->connect = pending;  	id = a2dp_source_request_stream(sink->session, dev, FALSE, -					stream_setup_complete, pending, NULL); +					stream_setup_complete, pending, NULL, +					NULL);  	if (id == 0) {  		pending_request_free(pending);  		sink->connect = NULL; diff --git a/audio/unix.c b/audio/unix.c index 6c420a24..ef8d6673 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -67,6 +67,7 @@ struct a2dp_data {  struct unix_client {  	struct device *dev;  	struct avdtp_local_sep *sep; +	struct avdtp_service_capability *media_codec;  	service_type_t type;  	char *interface;  	union { @@ -106,6 +107,8 @@ static void client_free(struct unix_client *client)  	if (client->sock >= 0)  		close(client->sock); +	if (client->media_codec) +		g_free(client->media_codec);  	g_free(client->interface);  	g_free(client);  } @@ -236,7 +239,6 @@ static int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd)  	return 0;  } -  static void headset_setup_complete(struct device *dev, void *user_data)  {  	struct unix_client *client = user_data; @@ -405,7 +407,8 @@ static void create_stream(struct device *dev, struct unix_client *client)  		id = a2dp_source_request_stream(a2dp->session, dev,  						TRUE, a2dp_setup_complete, -						client, &a2dp->sep); +						client, &a2dp->sep, +						client->media_codec);  		client->cancel_stream = a2dp_source_cancel_stream;  		break;  	case TYPE_HEADSET: @@ -442,11 +445,126 @@ static void create_cb(struct device *dev, void *user_data)  		create_stream(dev, client);  } +static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) +{ +	struct ipc_codec_sbc *sbc = (void *) cfg->data; + +	sbc_cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; +	sbc_cap->cap.media_codec_type = A2DP_CODEC_SBC; + +	if (cfg->rate > 0) { +		switch (cfg->rate) { +		case 48000: +			sbc_cap->frequency = A2DP_SAMPLING_FREQ_48000; +			break; +		case 44100: +			sbc_cap->frequency = A2DP_SAMPLING_FREQ_44100; +			break; +		case 32000: +			sbc_cap->frequency = A2DP_SAMPLING_FREQ_32000; +			break; +		case 16000: +			sbc_cap->frequency = A2DP_SAMPLING_FREQ_16000; +			break; +		default: +			return -EINVAL; +		} +	} else { +		sbc_cap->frequency = ( A2DP_SAMPLING_FREQ_48000 | +					A2DP_SAMPLING_FREQ_44100 | +					A2DP_SAMPLING_FREQ_32000 | +					A2DP_SAMPLING_FREQ_16000 ); +	} + +	if (cfg->channel_mode > 0) { +		switch (cfg->channel_mode) { +		case A2DP_CHANNEL_MODE_JOINT_STEREO: +		case A2DP_CHANNEL_MODE_STEREO: +		case A2DP_CHANNEL_MODE_DUAL_CHANNEL: +		case A2DP_CHANNEL_MODE_MONO: +			sbc_cap->channel_mode = cfg->channel_mode; +			break; +		default: +			return -EINVAL; +		} +	} else { +		sbc_cap->channel_mode = ( A2DP_CHANNEL_MODE_JOINT_STEREO | +					A2DP_CHANNEL_MODE_STEREO | +					A2DP_CHANNEL_MODE_DUAL_CHANNEL | +					A2DP_CHANNEL_MODE_MONO ); +	} + +	if (sbc->allocation > 0) { +		switch (sbc->allocation) { +		case A2DP_ALLOCATION_LOUDNESS: +		case A2DP_ALLOCATION_SNR: +			sbc_cap->allocation_method = sbc->allocation; +			break; +		default: +			return -EINVAL; +		} +	} else +		sbc_cap->allocation_method = ( A2DP_ALLOCATION_LOUDNESS | +						A2DP_ALLOCATION_SNR ); + +	if (sbc->subbands > 0) { +		switch (sbc->subbands) { +		case 8: +			sbc_cap->subbands = A2DP_SUBBANDS_8; +			break; +		case 4: +			sbc_cap->subbands = A2DP_SUBBANDS_4; +			break; +		default: +			return -EINVAL; +		} +	} else +		sbc_cap->subbands = ( A2DP_SUBBANDS_8 | A2DP_SUBBANDS_4 ); + +	if (sbc->blocks > 0) { +		switch (sbc->blocks) { +		case 16: +			sbc_cap->block_length = A2DP_BLOCK_LENGTH_16; +			break; +		case 12: +			sbc_cap->block_length = A2DP_BLOCK_LENGTH_12; +			break; +		case 8: +			sbc_cap->block_length = A2DP_BLOCK_LENGTH_8; +			break; +		case 4: +			sbc_cap->block_length = A2DP_BLOCK_LENGTH_4; +			break; +		default: +			return -EINVAL; +		} +	} else { +		sbc_cap->block_length = ( A2DP_BLOCK_LENGTH_16 | +					A2DP_BLOCK_LENGTH_12 | +					A2DP_BLOCK_LENGTH_8 | +					A2DP_BLOCK_LENGTH_4 ); +	} + +	if (sbc->bitpool > 250) +		return -EINVAL; +	else if (sbc->bitpool > 0) +		sbc_cap->min_bitpool = sbc_cap->max_bitpool = sbc->bitpool; +	else { +		sbc_cap->min_bitpool = 2; +		sbc_cap->max_bitpool = 250; +	} + +	return 0; +} + +  static void cfg_event(struct unix_client *client, struct ipc_packet *pkt,  			int len)  {  	struct device *dev;  	bdaddr_t bdaddr; +	struct ipc_data_cfg *cfg = (void *) pkt->data; +	struct sbc_codec_cap sbc_cap;  	str2ba(pkt->device, &bdaddr); @@ -460,6 +578,12 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt,  	else if (pkt->role == PKT_ROLE_HIFI)  		client->interface = g_strdup(AUDIO_SINK_INTERFACE); +	if (cfg_to_caps(cfg, &sbc_cap) < 0) +		goto failed; + +	client->media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, +							sizeof(sbc_cap)); +  	if (!manager_find_device(&bdaddr, NULL, FALSE)) {  		if (!bacmp(&bdaddr, BDADDR_ANY))  			goto failed; @@ -475,6 +599,7 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt,  		goto failed;  	create_stream(dev, client); +  	return;  failed: | 
