diff options
Diffstat (limited to 'audio/pcm_bluetooth.c')
| -rw-r--r-- | audio/pcm_bluetooth.c | 541 | 
1 files changed, 417 insertions, 124 deletions
diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 92d0383c..3f428ecd 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -27,6 +27,9 @@  #include <sys/socket.h>  #include <sys/un.h> +#include <sys/time.h> + +#include <netinet/in.h>  #include <alsa/asoundlib.h>  #include <alsa/pcm_external.h> @@ -35,6 +38,11 @@  #include <bluetooth/sco.h>  #include "ipc.h" +#include "sbc.h" + +/*#define ENABLE_DEBUG */ + +#define BUFFER_SIZE 1024  #ifdef ENABLE_DEBUG  #define DBG(fmt, arg...)  printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) @@ -50,15 +58,65 @@  #define SCO_RXBUFS 0x04  #endif +struct rtp_header { +	uint8_t cc:4; +	uint8_t x:1; +	uint8_t p:1; +	uint8_t v:2; + +	uint8_t pt:7; +	uint8_t m:1; + +	uint16_t sequence_number; +	uint32_t timestamp; +	uint32_t ssrc; +	uint32_t csrc[0]; +} __attribute__ ((packed)); + +struct rtp_payload { +	uint8_t frame_count:4; +	uint8_t rfa0:1; +	uint8_t is_last_fragment:1; +	uint8_t is_first_fragment:1; +	uint8_t is_fragmented:1; +} __attribute__ ((packed)); + +struct bluetooth_a2dp { +	sbc_t sbc;			/* Codec data */ +	int samples;			/* Number of encoded samples */ +	time_t timestamp;		/* Codec samples timestamp */ +	uint8_t buffer[BUFFER_SIZE];	/* Codec transfer buffer */ +	int count;			/* Codec transfer buffer counter */ + +	int nsamples;			/* Cumulative number of codec samples */ +	struct timeval ntimestamp;	/* Cumulative timeval */ +	uint16_t seq_num;		/* */ +	int frame_count;		/* */ + +	int bandwithcount; +	struct timeval bandwithtimestamp; +}; +  struct bluetooth_data {  	snd_pcm_ioplug_t io;  	snd_pcm_sframes_t hw_ptr;  	struct ipc_data_cfg cfg;	/* Bluetooth device config */  	int sock;			/* Daemon unix socket */ -	uint8_t *buffer;		/* Transfer buffer */ -	uint8_t count;			/* Transfer buffer counter */ +	uint8_t buffer[BUFFER_SIZE];	/* Encoded transfer buffer */ +	int count;			/* Transfer buffer counter */ +	struct bluetooth_a2dp a2dp;	/* a2dp data */  }; +void memcpy_changeendian(void *dst, const void *src, int size) +{ +	int i; +	const uint16_t *ptrsrc = src; +	uint16_t *ptrdst = dst; +	for (i = 0; i < size / 2; i++) { +		*ptrdst++ = htons(*ptrsrc++); +	} +} +  static int bluetooth_start(snd_pcm_ioplug_t *io)  {  	DBG("bluetooth_start %p", io); @@ -77,49 +135,21 @@ static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io)  {  	struct bluetooth_data *data = io->private_data; -	DBG("bluetooth_pointer %p", io); - -	DBG("hw_ptr=%lu", data->hw_ptr); +#if 0 +	DBG("bluetooth_pointer %p, hw_ptr=%lu", io, data->hw_ptr); +#endif  	return data->hw_ptr;  }  static void bluetooth_exit(struct bluetooth_data *data)  { -	int ret, len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_state); -	struct ipc_packet *pkt; -	struct ipc_data_state *state; - -	DBG("Sending PKT_TYPE_STATUS_REQ..."); - -	if ((pkt = malloc(len)) == NULL) -		goto done; - -	memset(pkt, 0, len); -	pkt->type = PKT_TYPE_STATE_REQ; -	pkt->role = PKT_ROLE_NONE; -	pkt->error = PKT_ERROR_NONE; - -	state = (struct ipc_data_state *) pkt->data; -	state->state = STATE_DISCONNECTED; - -	if ((ret = send(data->sock, pkt, len, 0)) < 0) -		DBG("Error %s (%d)", strerror(errno), errno); - -	free(pkt); -done: -	if (data == NULL) -		return; -  	if (data->sock >= 0)  		close(data->sock);  	if (data->cfg.fd >= 0)  		close(data->cfg.fd); -	if (data->buffer) -		free(data->buffer); -  	free(data);  } @@ -127,7 +157,7 @@ static int bluetooth_close(snd_pcm_ioplug_t *io)  {  	struct bluetooth_data *data = io->private_data; -	DBG("bluetooth_close %p", io); +	DBG("%p", io);  	bluetooth_exit(data); @@ -170,7 +200,7 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params  		return 0;  	opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? -		SO_SNDBUF : SO_RCVBUF; +			SO_SNDBUF : SO_RCVBUF;  	if (setsockopt(cfg.fd, SOL_SCO, opt_name, &period_count,  			sizeof(period_count)) == 0) @@ -178,14 +208,15 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params  	err = errno;  	SNDERR("%s (%d)", strerror(err), err); +	bluetooth_close(io);  	return -err;  } -static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io, -					const snd_pcm_channel_area_t *areas, -					snd_pcm_uframes_t offset, -					snd_pcm_uframes_t size) +static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, +						const snd_pcm_channel_area_t *areas, +						snd_pcm_uframes_t offset, +						snd_pcm_uframes_t size)  {  	struct bluetooth_data *data = io->private_data;  	struct ipc_data_cfg cfg = data->cfg; @@ -193,8 +224,9 @@ static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io,  	unsigned char *buff;  	int nrecv, frame_size = 0; -	DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu, io->nonblock=%u", -		areas->step, areas->first, offset, size, io->nonblock); +	DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," +		"io->nonblock=%u", areas->step, areas->first, offset, size, +		io->nonblock);  	if (data->count > 0)  		goto proceed; @@ -216,10 +248,12 @@ static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io,  	}  	/* Increment hardware transmition pointer */ -	data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % io->buffer_size; +	data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % +			io->buffer_size;  proceed: -	buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; +	buff = (unsigned char *) areas->addr + +			(areas->first + areas->step * offset) / 8;  	if ((data->count + size * frame_size) <= cfg.pkt_len)  		frames_to_write = size; @@ -238,10 +272,10 @@ done:  	return ret;  } -static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, -					const snd_pcm_channel_area_t *areas, -					snd_pcm_uframes_t offset, -					snd_pcm_uframes_t size) +static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, +						const snd_pcm_channel_area_t *areas, +						snd_pcm_uframes_t offset, +						snd_pcm_uframes_t size)  {  	struct bluetooth_data *data = io->private_data;  	struct ipc_data_cfg cfg = data->cfg; @@ -263,10 +297,11 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io,  	DBG("count = %d, frames_to_read = %lu", data->count, frames_to_read);  	/* Ready for more data */ -	buff = (uint8_t *) areas->addr + (areas->first + areas->step * offset) / 8; +	buff = (uint8_t *) areas->addr + +			(areas->first + areas->step * offset) / 8;  	memcpy(data->buffer + data->count, buff, frame_size * frames_to_read); -	/* Remember we have some frame in the pipe now */ +	/* Remember we have some frames in the pipe now */  	data->count += frames_to_read * frame_size;  	if (data->count != cfg.pkt_len) {  		ret = frames_to_read; @@ -290,28 +325,245 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io,  		ret = -EIO;  done: -	DBG("returning %d", (int)ret); +	DBG("returning %lu", ret); +	return ret; +} + +static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io, +						const snd_pcm_channel_area_t *areas, +						snd_pcm_uframes_t offset, +						snd_pcm_uframes_t size) +{ +	snd_pcm_uframes_t ret = 0; +	return ret; +} + +static int avdtp_write(struct bluetooth_a2dp *a2dp, struct ipc_data_cfg *cfg, +			unsigned int nonblock) +{ +	int count = 0; +	int written; +	struct rtp_header *header; +	struct rtp_payload *payload; +#ifdef ENABLE_DEBUG +	static struct timeval send_date = { 0, 0 }; +	static struct timeval prev_date = { 0, 0 }; +	struct timeval send_delay = { 0, 0 }; +	struct timeval sendz_delay = { 0, 0 }; +#endif + +	header = (void *) a2dp->buffer; +	payload = (void *) (a2dp->buffer + sizeof(*header)); + +	memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload)); + +	payload->frame_count = a2dp->frame_count; +	header->v = 2; +	header->pt = 1; +	header->sequence_number = htons(a2dp->seq_num); +	header->timestamp = htonl(a2dp->nsamples); +	header->ssrc = htonl(1); + +	while (count++ < 50) { +#ifdef ENABLE_DEBUG +		gettimeofday(&send_date, NULL); +#endif +		written = send(cfg->fd, a2dp->buffer, a2dp->count, +				nonblock ? MSG_DONTWAIT : 0); + +#ifdef ENABLE_DEBUG +		if ((written >= 0 || errno == EAGAIN) && prev_date.tv_sec != 0) { +			long delay, real, theo, delta; + +			delay = (long) (send_delay.tv_sec * 1000 + +						send_delay.tv_usec / 1000), +			real = (long) (sendz_delay.tv_sec * 1000 + +						sendz_delay.tv_usec / 1000); +			theo = (long) (((float) a2dp->nsamples) / +						cfg->rate * 1000.0); +			delta = (long) (sendz_delay.tv_sec * 1000 + +						sendz_delay.tv_usec / 1000) - +					(long) (((float) a2dp->nsamples) / +							cfg->rate * 1000.0); + +			timersub(&send_date, &prev_date, &send_delay); +			timersub(&send_date, &a2dp->ntimestamp, &sendz_delay); + +			DBG("send %d (cumul=%d) samples (delay=%ld ms," +					" real=%ld ms, theo=%ld ms," +					" delta=%ld ms).", a2dp->samples, +					a2dp->nsamples, delay, real, theo, +					delta); +		} +#endif +		if (written >= 0) +			break; + +		if (errno != EAGAIN) +			break; + +		DBG("send (retry)."); +		usleep(150000); +	} + +#ifdef ENABLE_DEBUG +	prev_date = send_date; +#endif + +	/* Send our data */ +	if (written != a2dp->count) +		DBG("Wrote %d not %d bytes", written, a2dp->count); +#if 0 +	else { +		/* Measure bandwith usage */ +		struct timeval now = { 0, 0 }; +		struct timeval interval = { 0, 0 }; + +		if(a2dp->bandwithtimestamp.tv_sec == 0) +			gettimeofday(&a2dp->bandwithtimestamp, NULL); + +		/* See if we must wait again */ +		gettimeofday(&now, NULL); +		timersub(&now, &a2dp->bandwithtimestamp, &interval); +		if(interval.tv_sec > 0) +			DBG("Bandwith: %d (%d kbps)", a2dp->bandwithcount, +				a2dp->bandwithcount/128); +		a2dp->bandwithtimestamp = now; +		a2dp->bandwithcount = 0; +	} + +	a2dp->bandwithcount += written; + +#endif +	/* Reset buffer of data to send */ +	a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); +	a2dp->frame_count = 0; +	a2dp->samples = 0; +	a2dp->seq_num++; + +	return written; +} + +static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, +						const snd_pcm_channel_area_t *areas, +						snd_pcm_uframes_t offset, +						snd_pcm_uframes_t size) +{ +	struct bluetooth_data *data = io->private_data; +	struct bluetooth_a2dp *a2dp = &data->a2dp; +	snd_pcm_sframes_t ret = 0; +	snd_pcm_uframes_t frames_to_read; +	int frame_size, encoded; +	uint8_t *buff; +	static int codesize = 0; + +	DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," +			"io->nonblock=%u", areas->step, areas->first, +			offset, size, io->nonblock); + +	if (codesize == 0) { +		/* How much data can be encoded by sbc at a time? */ +		codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * +				a2dp->sbc.channels * 2; +		/* Reserv header space in outgoing buffer */ +		a2dp->count = sizeof(struct rtp_header) + +				sizeof(struct rtp_payload); +		gettimeofday(&a2dp->ntimestamp, NULL); +	} + +	frame_size = areas->step / 8; +	if ((data->count + size * frame_size) <= codesize) +		frames_to_read = size; +	else +		frames_to_read = (codesize - data->count) / frame_size; + +	DBG("count = %d, frames_to_read = %lu", data->count, frames_to_read); +	DBG("a2dp.count = %d cfg.pkt_len = %d", a2dp->count, +			data->cfg.pkt_len); + +	/* FIXME: If state is not streaming then return */ + +	/* Ready for more data */ +	buff = (uint8_t *) areas->addr + +		(areas->first + areas->step * offset) / 8; +	memcpy_changeendian(data->buffer + data->count, buff, +				frame_size * frames_to_read); + +	/* Remember we have some frames in the pipe now */ +	data->count += frames_to_read * frame_size; +	if (data->count != codesize) { +		ret = frames_to_read; +		goto done; +	} + +	/* Enough data to encode (sbc wants 1k blocks) */ +	encoded = sbc_encode(&(a2dp->sbc), data->buffer, codesize); +	if (encoded <= 0) { +		DBG("Encoding error %d", encoded); +		goto done; +	} + +	data->count -= encoded; + +	DBG("encoded = %d  a2dp.sbc.len= %d", encoded, a2dp->sbc.len); + +	if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) +		avdtp_write(a2dp, &data->cfg, io->nonblock); + +	memcpy(a2dp->buffer + a2dp->count, a2dp->sbc.data, a2dp->sbc.len); +	a2dp->count += a2dp->sbc.len; +	a2dp->frame_count++; +	a2dp->samples += encoded / frame_size; +	a2dp->nsamples += encoded / frame_size; +	/* Increment hardware transmition pointer */ +	data->hw_ptr = (data->hw_ptr + codesize / frame_size) +			% io->buffer_size; + +	ret = frames_to_read; + +done: +	DBG("returning %lu", ret);  	return ret;  } -static snd_pcm_ioplug_callback_t bluetooth_playback_callback = { +static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { +	.start		= bluetooth_start, +	.stop		= bluetooth_stop, +	.pointer	= bluetooth_pointer, +	.close		= bluetooth_close, +	.hw_params	= bluetooth_hw_params, +	.prepare	= bluetooth_prepare, +	.transfer	= bluetooth_hsp_write, +}; + +static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = {  	.start		= bluetooth_start,  	.stop		= bluetooth_stop,  	.pointer	= bluetooth_pointer,  	.close		= bluetooth_close,  	.hw_params	= bluetooth_hw_params,  	.prepare	= bluetooth_prepare, -	.transfer	= bluetooth_write, +	.transfer	= bluetooth_hsp_read,  }; -static snd_pcm_ioplug_callback_t bluetooth_capture_callback = { +static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = {  	.start		= bluetooth_start,  	.stop		= bluetooth_stop,  	.pointer	= bluetooth_pointer,  	.close		= bluetooth_close,  	.hw_params	= bluetooth_hw_params,  	.prepare	= bluetooth_prepare, -	.transfer	= bluetooth_read, +	.transfer	= bluetooth_a2dp_write, +}; + +static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { +	.start		= bluetooth_start, +	.stop		= bluetooth_stop, +	.pointer	= bluetooth_pointer, +	.close		= bluetooth_close, +	.hw_params	= bluetooth_hw_params, +	.prepare	= bluetooth_prepare, +	.transfer	= bluetooth_a2dp_read,  };  #define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0])) @@ -390,7 +642,6 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data)  	};  	ret = recvmsg(data->sock, &msgh, 0); -  	if (ret < 0) {  		err = errno;  		SNDERR("Unable to receive fd: %s (%d)", strerror(err), err); @@ -415,102 +666,143 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data)  	return -EINVAL;  } -static int bluetooth_cfg(struct bluetooth_data *data) +static int bluetooth_a2dp_init(struct bluetooth_data *data, +				struct ipc_codec_sbc *sbc)  { -	int ret, len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); -	struct ipc_packet *pkt; +	struct bluetooth_a2dp *a2dp = &data->a2dp; +	struct ipc_data_cfg *cfg = &data->cfg; -	DBG("Sending PKT_TYPE_CFG_REQ..."); +	if (cfg == NULL) { +		SNDERR("Error getting codec parameters"); +		return -1; +	} -	if ((pkt = malloc(len)) == 0) -		return -ENOMEM; +	if (cfg->codec != CFG_CODEC_SBC) +		return -1; + +	/* FIXME: init using flags? */ +	sbc_init(&a2dp->sbc, 0); +	a2dp->sbc.rate = cfg->rate; +	a2dp->sbc.channels = cfg->channels; +	if (cfg->channel_mode == CFG_CHANNEL_MODE_MONO || +			cfg->channel_mode == CFG_CHANNEL_MODE_JOINT_STEREO) +		a2dp->sbc.joint = 1; +	a2dp->sbc.allocation = sbc->allocation; +	a2dp->sbc.subbands = sbc->subbands; +	a2dp->sbc.blocks = sbc->blocks; +	a2dp->sbc.bitpool = sbc->bitpool; -	memset(pkt, 0, len); +	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));  	pkt->type = PKT_TYPE_CFG_REQ;  	pkt->role = PKT_ROLE_NONE;  	pkt->error = PKT_ERROR_NONE; -	if ((ret = send(data->sock, pkt, len, 0)) < 0) { -		ret = -errno; -		goto done; -	} else if (ret == 0) { -		ret = -EIO; -		goto done; -	} +	ret = send(data->sock, pkt, sizeof(struct ipc_packet), 0); +	if (ret < 0) +		return -errno; +	else if (ret == 0) +		return -EIO; -	DBG("OK - %d bytes sent", ret); +	DBG("OK - %d bytes sent. Waiting for response...", ret); -	DBG("Waiting for response..."); +	memset(buf, 0, sizeof(buf)); -	memset(pkt, 0, len); -	if ((ret = recv(data->sock, pkt, len, 0)) < 0) { -		ret = -errno; -		goto done; -	} else if (ret == 0) { -		ret = -EIO; -		goto done; -	} +	ret = recv(data->sock, buf, sizeof(*pkt) + sizeof(*cfg), 0); +	if (ret < 0) +		return -errno; +	else if (ret == 0) +		return -EIO; -	DBG("OK - %d bytes received", ret); +	total = ret;  	if (pkt->type != PKT_TYPE_CFG_RSP) {  		SNDERR("Unexpected packet type received: type = %d",  				pkt->type); -		ret = -EINVAL; -		goto done; +		return -EINVAL;  	}  	if (pkt->error != PKT_ERROR_NONE) {  		SNDERR("Error while configuring device: error = %d",  				pkt->error); -		ret = pkt->error; -		goto done; +		return pkt->error;  	} -	if (pkt->length != sizeof(struct ipc_data_cfg)) { +	if (cfg->codec != CFG_CODEC_SBC) +		goto done; + +	ret = recv(data->sock, sbc, sizeof(*sbc), 0); +	if (ret < 0) +		return -errno; +	else if (ret == 0) +		return -EIO; + +	total += ret; + +done: +	DBG("OK - %d bytes received", total); + +	if (pkt->length != (total - sizeof(struct ipc_packet))) {  		SNDERR("Error while configuring device: packet size doesn't "  				"match"); -		ret = -EINVAL; -		goto done; +		return -EINVAL;  	} -	memcpy(&data->cfg, pkt->data, sizeof(struct ipc_data_cfg)); +	memcpy(&data->cfg, cfg, sizeof(*cfg));  	DBG("Device configuration:"); -	DBG("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u, sample_size=%u," -			"rate=%u", data->cfg.fd, data->cfg.fd_opt, -			data->cfg.channels, data->cfg.pkt_len, -			data->cfg.sample_size, data->cfg.rate); +	DBG("\n\tfd=%d\n\tfd_opt=%u\n\tchannels=%u\n\tpkt_len=%u\n" +		"\tsample_size=%u\n\trate=%u", data->cfg.fd, +		data->cfg.fd_opt, data->cfg.channels, data->cfg.pkt_len, +		data->cfg.sample_size, data->cfg.rate); + +	if (data->cfg.codec == CFG_CODEC_SBC) { +		struct bluetooth_a2dp *a2dp = &data->a2dp; +		ret = bluetooth_a2dp_init(data, sbc); +		if (ret < 0) +			return ret; +		printf("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\t" +				"bitpool=%u\n", a2dp->sbc.allocation, +				a2dp->sbc.subbands, a2dp->sbc.blocks, +				a2dp->sbc.bitpool); +	}  	if (data->cfg.fd == -1) {  		SNDERR("Error while configuring device: could not acquire "  				"audio socket"); -		ret = -EINVAL; -		goto done; +		return -EINVAL;  	} -	if ((ret = bluetooth_recvmsg_fd(data)) < 0) -		goto done; - -	if ((data->buffer = malloc(data->cfg.pkt_len)) == 0) -		return -ENOMEM; +	ret = bluetooth_recvmsg_fd(data); +	if (ret < 0) +		return ret;  	/* It is possible there is some outstanding  	data in the pipe - we have to empty it */ -	while(recv(data->cfg.fd, data->buffer, data->cfg.pkt_len, -		MSG_DONTWAIT) > 0); +	while (recv(data->cfg.fd, data->buffer, data->cfg.pkt_len, +				MSG_DONTWAIT) > 0);  	memset(data->buffer, 0, data->cfg.pkt_len); -done: -	free(pkt); -	return ret; +	return 0;  } -static int bluetooth_init(struct bluetooth_data *data) +static int bluetooth_init(struct bluetooth_data *data, snd_config_t *conf)  { -	int sk, err, id; +	int sk, err;  	struct sockaddr_un addr = {  		AF_UNIX, IPC_SOCKET_NAME  	}; @@ -522,28 +814,24 @@ static int bluetooth_init(struct bluetooth_data *data)  	data->sock = -1; -	id = abs(getpid() * rand()); - -	if ((sk = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { -		err = -errno; -		SNDERR("Can't open socket"); -		return -errno; +	sk = socket(PF_LOCAL, SOCK_STREAM, 0); +	if (sk < 0) { +		err = errno; +		SNDERR("Cannot open socket: %s (%d)", strerror(err), err); +		return -err;  	}  	DBG("Connecting to address: %s", addr.sun_path + 1);  	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { -		err = -errno; -		SNDERR("Can't connect socket"); +		err = errno; +		SNDERR("Connection fail", strerror(err), err);  		close(sk); -		return err; +		return -err;  	}  	data->sock = sk; -	if ((err = bluetooth_cfg(data)) < 0) -		return err; - -	return 0; +	return bluetooth_cfg(data, conf);  }  SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) @@ -555,27 +843,32 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)  		stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture");  	data = malloc(sizeof(struct bluetooth_data)); -	memset(data, 0, sizeof(struct bluetooth_data));  	if (!data) {  		err = -ENOMEM;  		goto error;  	} -	err = bluetooth_init(data); +	err = bluetooth_init(data, conf);  	if (err < 0)  		goto error;  	data->io.version = SND_PCM_IOPLUG_VERSION;  	data->io.name = "Bluetooth Audio Device";  	data->io.mmap_rw = 0; /* No direct mmap communication */ - -	data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? -		&bluetooth_playback_callback : &bluetooth_capture_callback;  	data->io.poll_fd = data->cfg.fd;  	data->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ?  					POLLOUT : POLLIN;  	data->io.private_data = data; +	if (data->cfg.codec == CFG_CODEC_SBC) +		data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? +			&bluetooth_a2dp_playback : +			&bluetooth_a2dp_capture; +	else +		data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? +			&bluetooth_hsp_playback : +			&bluetooth_hsp_capture; +  	err = snd_pcm_ioplug_create(&data->io, name, stream, mode);  	if (err < 0)  		goto error;  | 
