diff options
| -rw-r--r-- | audio/ipc.h | 10 | ||||
| -rw-r--r-- | audio/pcm_bluetooth.c | 317 | ||||
| -rw-r--r-- | audio/unix.c | 34 | 
3 files changed, 307 insertions, 54 deletions
| diff --git a/audio/ipc.h b/audio/ipc.h index e6859a50..0cd9e620 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -40,10 +40,10 @@  /* Packet types */  #define PKT_TYPE_CFG_REQ	0  #define PKT_TYPE_CFG_RSP	1 -#define PKT_TYPE_STATUS_REQ	3 -#define PKT_TYPE_STATUS_RSP	4 -#define PKT_TYPE_CTL_REQ	5 -#define PKT_TYPE_CTL_RSP	6 +#define PKT_TYPE_STATUS_REQ	2 +#define PKT_TYPE_STATUS_RSP	3 +#define PKT_TYPE_CTL_REQ	4 +#define PKT_TYPE_CTL_RSP	5  /* Errors codes */  #define PKT_ERROR_NONE		0 @@ -68,6 +68,8 @@ struct ipc_data_cfg {  	uint8_t encoding;	/* Stream encoding */  	uint8_t bitpool;	/* Encoding bitpool */  	uint8_t channels;	/* Number of audio channel */ +	uint8_t pkt_len;	/* Stream packet length */ +	uint8_t sample_size;	/* Sample size in bytes */  	uint16_t rate;		/* Stream sample rate */  } __attribute__ ((packed)); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 838dcd39..29583940 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -31,14 +31,28 @@  #include <alsa/asoundlib.h>  #include <alsa/pcm_external.h> +#include <bluetooth/bluetooth.h> +#include <bluetooth/sco.h> +  #include "ipc.h"  #define DBG(fmt, arg...)  printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) +#ifndef SCO_TXBUFS +#define SCO_TXBUFS 0x03 +#endif + +#ifndef SCO_RXBUFS +#define SCO_RXBUFS 0x04 +#endif +  struct bluetooth_data {  	snd_pcm_ioplug_t io;  	snd_pcm_sframes_t hw_ptr; -	int sock; +	struct ipc_data_cfg cfg;	/* Bluetooth device config */ +	int sock;			/* Daemon unix socket */ +	uint8_t *buffer;		/* Transfer buffer */ +	uint8_t count;			/* Transfer buffer counter */  };  static int bluetooth_start(snd_pcm_ioplug_t *io) @@ -59,9 +73,9 @@ static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io)  {  	struct bluetooth_data *data = io->private_data; -	//DBG("io %p", io); +	DBG("io %p", io); -	//DBG("hw_ptr=%lu", data->hw_ptr); +	DBG("hw_ptr=%lu", data->hw_ptr);  	return data->hw_ptr;  } @@ -77,16 +91,171 @@ static int bluetooth_close(snd_pcm_ioplug_t *io)  	return 0;  } + +static int bluetooth_prepare(snd_pcm_ioplug_t *io) +{ +	struct bluetooth_data *data = io->private_data; + +	DBG("Preparing with io->period_size = %lu, io->buffer_size = %lu", io->period_size, io->buffer_size); + +	if (io->stream == SND_PCM_STREAM_PLAYBACK) { +		/* If not null for playback, xmms doesn't display time correctly */ +		data->hw_ptr = 0; +	} +	else { +		/* ALSA library is really picky on the fact hw_ptr is not null. If it is, capture won't start */ +		data->hw_ptr = io->period_size; +	} +	return 0; +} + +static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) +{ +	struct bluetooth_data *data = io->private_data; +	struct ipc_data_cfg cfg = data->cfg; +	uint32_t period_count = io->buffer_size / io->period_size; + +	DBG("period_count = %d", period_count); + +	if(setsockopt(cfg.fd, SOL_SCO, +			io->stream == SND_PCM_STREAM_PLAYBACK ? SCO_TXBUFS : SCO_RXBUFS, +			&period_count, +			sizeof(period_count)) == 0) { +		return 0; +	} else if(setsockopt(cfg.fd, SOL_SCO, +			io->stream == SND_PCM_STREAM_PLAYBACK ? SO_SNDBUF : SO_RCVBUF, +			&period_count, +			sizeof(period_count)) == 0) { +                return 0; +        } else { +		SNDERR("Unable to set number of SCO buffers : please upgrade your Kernel !"); +		return -EINVAL; +	} +} + +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) +{ +	struct bluetooth_data *data = io->private_data; +	struct ipc_data_cfg cfg = data->cfg; + +	snd_pcm_sframes_t ret = 0; + +	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) { +		int nrecv; + +		nrecv = recv(cfg.fd, data->buffer, cfg.pkt_len, +			MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0 )); + +		if (nrecv == cfg.pkt_len) { +			ret = 0; +			/* Increment hardware transmition pointer */ +			data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % io->buffer_size; +		} +		else if (nrecv > 0) { +			ret = -EIO; +			SNDERR(strerror(-ret)); +		} +		else if (nrecv == -1 && errno == EAGAIN) { +			ret = -EAGAIN; +		} +		else { /* nrecv < 0 */ +			/* EPIPE means device underrun in ALSA world. But we mean we lost contact +			with server, so we have to find another error code */ +			ret = (errno == EPIPE ? -EIO : -errno); +			SYSERR("Lost contact with headsetd"); +		} +	} +	if(ret == 0) { /* Still ok, proceed */ +		snd_pcm_uframes_t frames_to_write; +		unsigned char *buff; + +		buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; + +		if((data->count + cfg.sample_size * size) <= cfg.pkt_len) +			frames_to_write = size; +		else +			frames_to_write = (cfg.pkt_len - data->count) / cfg.sample_size; + +		memcpy(buff, data->buffer + data->count, areas->step / 8 * frames_to_write); +		data->count += (areas->step / 8 * frames_to_write); +		data->count %= cfg.pkt_len; +		/* Return written frames count */ +		ret = frames_to_write; +	} + +	DBG("returning %d", (int)ret); +	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) +{ +	struct bluetooth_data *data = io->private_data; +	struct ipc_data_cfg cfg = data->cfg; +	snd_pcm_sframes_t ret = 0; +	snd_pcm_uframes_t frames_to_read; +	unsigned char *buff; + +	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 + cfg.sample_size * size) <= cfg.pkt_len) +		frames_to_read = size; +	else +		frames_to_read = (cfg.pkt_len - data->count) / cfg.sample_size; + +	/* Ready for more data */ +	buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; +	memcpy(data->buffer + data->count, buff, areas->step / 8 * frames_to_read); + +	if ((data->count + areas->step / 8 * frames_to_read) == cfg.pkt_len) { +		int rsend; +		/* Actually send packet */ +		rsend = send(cfg.fd, data->buffer, cfg.pkt_len, io->nonblock ? MSG_DONTWAIT : 0); +		if (rsend > 0) { +			/* Reset count pointer */ +			data->count = 0; + +			/* Increment hardware transmition pointer */ +			data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % io->buffer_size; + +			ret = frames_to_read; +		} +		else { +			/* EPIPE means device underrun in ALSA world. But we mean we lost contact +                           with server, so we have to find another error code */ +			ret = (errno == EPIPE ? -EIO : -errno); +			if(errno == EPIPE) +				SYSERR("Lost contact with headsetd"); +		} +	} +	else { +		/* Remember we have some frame in the pipe now */ +		data->count += areas->step / 8 * frames_to_read; +		/* Ask for more */ +		ret = frames_to_read; +	} + +	DBG("returning %d", (int)ret); +	return ret; +} +  static snd_pcm_ioplug_callback_t bluetooth_playback_callback = {  	.start		= bluetooth_start,  	.stop		= bluetooth_stop,  	.pointer	= bluetooth_pointer,  	.close		= bluetooth_close, -#if 0  	.hw_params	= bluetooth_hw_params,  	.prepare	= bluetooth_prepare,  	.transfer	= bluetooth_write, -#endif  };  static snd_pcm_ioplug_callback_t bluetooth_capture_callback = { @@ -94,11 +263,9 @@ static snd_pcm_ioplug_callback_t bluetooth_capture_callback = {  	.stop		= bluetooth_stop,  	.pointer	= bluetooth_pointer,  	.close		= bluetooth_close, -#if 0  	.hw_params	= bluetooth_hw_params,  	.prepare	= bluetooth_prepare,  	.transfer	= bluetooth_read, -#endif  };  #define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0])) @@ -146,42 +313,66 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io)  	return 0;  } -SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) +static int bluetooth_cfg(struct bluetooth_data *data)  { -	snd_config_iterator_t iter, next; -	struct bluetooth_data *data; -	struct sockaddr_un addr; -	unsigned int id; -	int sk, err; - -	DBG("Bluetooth PCM plugin (%s)", -		stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); - -	snd_config_for_each(iter, next, conf) { -		snd_config_t *n = snd_config_iterator_entry(iter); -		const char *id; +	struct ipc_packet pkt; +	int res; + +	DBG("Sending PKT_TYPE_CFG_REQ..."); +	pkt.type = PKT_TYPE_CFG_REQ; +	pkt.role = PKT_ROLE_NONE; +	res = send(data->sock, &pkt, sizeof(struct ipc_packet), 0); +	if (res < 0) +		return errno; +	DBG("OK - %d bytes sent", res); + +	DBG("Waiting for response..."); +	do { +		int len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); +		struct ipc_packet *pkt_ptr; + +		pkt_ptr = malloc(sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg)); +		res = recv(data->sock, pkt_ptr, len, MSG_WAITALL | (data->io.nonblock ? MSG_DONTWAIT : 0 )); +	} while ((res < 0) && (errno == EINTR)); +	if (res < 0) +		return -errno; +	DBG("OK - %d bytes received", res); -		if (snd_config_get_id(n, &id) < 0) -			continue; +	if (pkt.type != PKT_TYPE_CFG_RSP) { +		SNDERR("Unexpected packet type received: type = %d", pkt.type); +		return -EINVAL; +	} -		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) -			continue; +	if (pkt.error != PKT_ERROR_NONE) { +		SNDERR("Error while configuring device: error = %d", pkt.error); +		return pkt.error; +	} -		if (strcmp(id, "bdaddr") == 0) { -			const char *str; -			if (snd_config_get_string(n, &str) < 0) { -				SNDERR("Invalid type for %s", id); -				return -EINVAL; -			} -			printf("bdaddr %s\n", str); -			continue; -		} +	if (pkt.length != sizeof(struct ipc_data_cfg)) { +		SNDERR("Error while configuring device: packet size doesn't match"); +		return -EINVAL; +	} -		SNDERR("Unknown field %s", id); +	memcpy(&data->cfg, &pkt.data, pkt.length); +	if (data->cfg.fd == -1) { +		SNDERR("Error while configuring device: could not acquire audio socket");  		return -EINVAL;  	} +	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); + +	return 0; +} + +static int bluetooth_init(struct bluetooth_data *data) +{ +	int sk, err, id; +	struct sockaddr_un addr; +  	id = abs(getpid() * rand());  	sk = socket(PF_LOCAL, SOCK_DGRAM, 0); @@ -195,6 +386,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)  	snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s/%d",  			IPC_SOCKET_NAME, id); +	DBG("Binding address: %s", addr.sun_path + 1);  	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {  		SNDERR("Can't bind socket");  		close(sk); @@ -205,6 +397,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)  	addr.sun_family = AF_UNIX;  	snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", IPC_SOCKET_NAME); +	DBG("Connecting to address: %s", addr.sun_path + 1);  	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {  		SNDERR("Can't connect socket");  		close(sk); @@ -221,34 +414,62 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)  	data->sock = sk; -	data->io.version = SND_PCM_IOPLUG_VERSION; -	data->io.name = "Bluetooth Audio"; -	data->io.mmap_rw =  0;		/* No direct mmap communication */ +	if ((err = bluetooth_cfg(data)) < 0) +		return err; + +	data->buffer = malloc(data->cfg.pkt_len); + +	memset(data->buffer, 0, data->cfg.pkt_len); -	data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? +	return 0; +} + +SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) +{ +//	snd_config_iterator_t iter, next; +	struct bluetooth_data data; +	int err; + +	DBG("Bluetooth PCM plugin blablabla (%s)", +		stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); + +//	snd_config_for_each(iter, next, conf) { +//	} + +	DBG("Initing Bluetooth..."); +	err = bluetooth_init(&data); +	if (err < 0) +		goto error; +	DBG("Done"); + +	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 = sk; -	data->io.poll_events = POLLIN; -	data->io.private_data = data; +	data.io.poll_fd = data.cfg.fd; +	data.io.poll_events = POLLIN; +	data.io.private_data = &data; -	err = snd_pcm_ioplug_create(&data->io, name, stream, mode); +	err = snd_pcm_ioplug_create(&data.io, name, stream, mode);  	if (err < 0)  		goto error; -	err = bluetooth_hw_constraint(&data->io); +	err = bluetooth_hw_constraint(&data.io);  	if (err < 0) { -		snd_pcm_ioplug_delete(&data->io); -		goto error;  +		snd_pcm_ioplug_delete(&data.io); +		goto error;  	} -	*pcmp = data->io.pcm; +	*pcmp = data.io.pcm;  	return 0;  error: -	close(sk); +	close(data.sock); -	free(data); +	free(&data);  	return err;  } diff --git a/audio/unix.c b/audio/unix.c index ce2041b2..8cd1affc 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -48,7 +48,8 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data)  {  	struct sockaddr_un addr;  	socklen_t addrlen; -	unsigned char buf[128]; +	struct ipc_packet pkt; +	struct ipc_data_cfg cfg;  	int sk, len;  	debug("chan %p cond %td data %p", chan, cond, data); @@ -66,10 +67,37 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data)  	memset(&addr, 0, sizeof(addr));  	addrlen = sizeof(addr); -	len = recvfrom(sk, buf, sizeof(buf), 0, (struct sockaddr *) &addr, &addrlen); +	len = recvfrom(sk, &pkt, sizeof(pkt), 0, (struct sockaddr *) &addr, &addrlen);  	debug("path %s len %d", addr.sun_path + 1, len); +	switch (pkt.type) { +	case PKT_TYPE_CFG_REQ: +		info("Package PKT_TYPE_CFG_REQ:%u", pkt.role); +		struct ipc_data_cfg *cfg_ptr; + +		cfg.fd = -1; +		cfg.fd_opt = CFG_FD_OPT_READWRITE; +		cfg.encoding = 0; +		cfg.bitpool = 0; +		cfg.channels = 1; +		cfg.pkt_len = 48; +		cfg.sample_size = 2; +		cfg.rate = 8000; + +		cfg_ptr = (struct ipc_data_cfg *) &pkt.data; +		pkt.type = PKT_TYPE_CFG_RSP; +		cfg_ptr[0] = cfg; +		pkt.length = sizeof(struct ipc_data_cfg); +		len = send(sk, &pkt, sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg), 0); +		break; +	case PKT_TYPE_STATUS_REQ: +		info("Package PKT_TYPE_STATUS_REQ"); +		break; +	case PKT_TYPE_CTL_REQ: +		info("Package PKT_TYPE_CTL_REQ"); +		break; +	}  	return TRUE;  } @@ -106,6 +134,8 @@ int unix_init(void)  	g_io_channel_unref(io); +	info("Unix socket created: %d", sk); +  	return 0;  } | 
