? Makefile
? Makefile.in
? a2play
? aclocal.m4
? autom4te.cache
? btsco
? btsco2
? config.h
? config.h.in
? config.log
? config.status
? configure
? install-sh
? missing
? mkinstalldirs
? stamp-h1
? avdtp/Makefile
? avdtp/Makefile.in
? avdtp/avtest
? kernel/.btsco.o.cmd
? kernel/.snd-bt-sco.ko.cmd
? kernel/.snd-bt-sco.mod.o.cmd
? kernel/.snd-bt-sco.o.cmd
? kernel/.tmp_versions
? kernel/snd-bt-sco.ko
? kernel/snd-bt-sco.mod.c
? sbc/Makefile
? sbc/Makefile.in
? sbc/rcplay
? sbc/sbcdec
? sbc/sbcenc
? sbc/sbcinfo
Index: btsco.c
===================================================================
RCS file: /cvsroot/bluetooth-alsa/btsco/btsco.c,v
retrieving revision 1.14
diff -u -r1.14 btsco.c
--- btsco.c	9 Mar 2005 23:11:35 -0000	1.14
+++ btsco.c	14 Mar 2005 14:55:25 -0000
@@ -32,6 +32,8 @@
 #include <assert.h>
 #include <string.h>
 #include <errno.h>
+#include <regex.h>
+#include <ctype.h>
 
 #include <sys/wait.h>
 #include <sys/time.h>
@@ -50,6 +52,7 @@
 #include <alsa/asoundlib.h>
 
 #define SNDRV_BT_SCO_IOCTL_SET_SCO_SOCKET _IOW ('H', 0x10, int)
+#define SNDRV_BT_SCO_IOCTL_REQ_INFO _IO ('H', 0x11)
 
 #ifndef SND_HWDEP_IFACE_EMUX_WAVETABLE
 #define SND_HWDEP_IFACE_EMUX_WAVETABLE (SND_HWDEP_IFACE_USX2Y + 1)
@@ -66,13 +69,35 @@
 #define NOT_CONNECTED 0
 #define CONNECTED 1
 
-static volatile int terminate = 0;
+typedef struct snd_card_bt_sco_info {
+	int mixer_volume[2];
+	int playback_count, capture_count;
+} snd_card_bt_sco_info_t;
+
+struct action {
+	struct action *next;
+	regex_t regex;
+	char *cmd;
+};
+
+static volatile int terminate = 0, ring = 0, hupped = 0;
+static int verbose = 0;
 
 static void sig_term(int sig)
 {
 	terminate = 1;
 }
 
+static void sig_ring(int sig)
+{
+	ring = 1;
+}
+
+static void sig_hup(int sig)
+{
+	hupped = 1;
+}
+
 static int rfcomm_connect(bdaddr_t * src, bdaddr_t * dst, uint8_t channel)
 {
 	struct sockaddr_rc addr;
@@ -266,7 +291,7 @@
 	sdp_uuid16_create(&group, 0x1108);
 	sess = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);
 	if (!sess) {
-		printf
+		error
 		    ("Failed to connect to SDP server: %s\nAssuming channel %d\n",
 		     strerror(errno), channel);
 		return channel;
@@ -281,7 +306,7 @@
 	sdp_list_free(search, 0);
 
 	if (searchresult) {
-		printf("Service Search failed: %s\nAssuming channel %d\n",
+		error("Service Search failed: %s\nAssuming channel %d\n",
 		       strerror(errno), channel);
 		sdp_close(sess);
 		return channel;
@@ -302,14 +327,107 @@
 	return channel;
 }
 
+static void free_actions(struct action *list)
+{
+	struct action *cur;
+	
+	while(list != NULL) {
+		cur = list;
+		list = cur->next;
+		regfree(&cur->regex);
+		free(cur->cmd);
+		free(cur);
+	}
+}
+
+static struct action *read_actions(void)
+{
+	int state, retval, len;
+	struct action *ret, *cur, *new;
+	char buf[1024];
+	char *p;
+	FILE *cf;
+	
+	ret = NULL;
+	if(getenv("HOME") == NULL)
+		return(NULL);
+	snprintf(buf, sizeof(buf), "%s/.btscorc", getenv("HOME"));
+	if((cf = fopen(buf, "r")) == NULL) {
+		if(errno != ENOENT)
+			perror(buf);
+		return(NULL);
+	}
+	state = 0;
+	while(!feof(cf)) {
+		if(fgets(buf, sizeof(buf), cf) == NULL) {
+			if(ferror(cf)) {
+				error("reading cf: %s", strerror(ferror(cf)));
+				free_actions(ret);
+				return(NULL);
+			} else {
+				continue;
+			}
+		}
+		
+		if(buf[0] == '#')
+			continue;
+		for(p = buf; isspace(*p); p++);
+		memmove(buf, p, strlen(buf) + 1 - (p - buf));
+		if(strlen(buf) == 0)
+			continue;
+		for(p = buf + strlen(buf) - 1; isspace(*p); p--);
+		p[1] = 0;
+		
+		switch(state) {
+		case 0:
+			new = malloc(sizeof(*new));
+			new->next = NULL;
+			new->cmd = NULL;
+			if((retval = regcomp(&new->regex, buf, REG_EXTENDED)) != 0) {
+				error("could not compile regex `%s'", buf);
+				free_actions(ret);
+				free(new);
+				return(NULL);
+			}
+			state = 1;
+			break;
+		case 1:
+			len = strlen(buf);
+			if(buf[len - 1] == '\\') {
+				buf[len - 1] = 0;
+			} else {
+				state = 0;
+				if(ret == NULL) {
+					ret = cur = new;
+				} else {
+					cur->next = new;
+					cur = new;
+				}
+			}
+			if(new->cmd == NULL) {
+				new->cmd = strdup(buf);
+			} else {
+				len = strlen(new->cmd);
+				new->cmd = realloc(new->cmd, len + strlen(buf) + 1);
+				memcpy(new->cmd + len, buf, strlen(buf));
+				new->cmd[len + strlen(buf)] = 0;
+			}
+			break;
+		}
+	}
+	fclose(cf);
+	return(ret);
+}
+
 int main(int argc, char *argv[])
 {
 	int dev;
 	int card;
+	int ret;
+	int fork, clear;
 
 	struct sigaction sa;
 
-	fd_set rfds;
 	//struct timeval timeout;
 	unsigned char buf[2048];
 	//int sel, rlen, wlen;
@@ -327,8 +445,8 @@
 	int sd;			//sco handle
 	uint16_t sco_handle, sco_mtu, vs;
 	char line[100];
-	int volumes[2], last_volumes[2];
-	int opdone;
+	int last_volumes[2];
+	int dr_usage, force_sco;
 
 	// sco_mode is our running mode. 0 => not connect, 1 => connected
 	// see NOT_CONNECTED,CONNECTED :)
@@ -341,14 +459,41 @@
 
 	snd_hwdep_t *handle;
 	char hwdep_name[16];
+	snd_card_bt_sco_info_t infobuf;
+	struct action *actions;
 
+	fork = 0;
+	clear = 0;
+	while((i = getopt(argc, argv, "fcvh")) >= 0) {
+		switch(i) {
+		case 'v':
+			verbose++;
+			break;
+		case 'f':
+			fork = 1;
+			break;
+		case 'c':
+			clear = 1;
+			break;
+		case 'h':
+		case '?':
+		case ':':
+		default:
+			usage();
+			exit((i == 'h')?0:1);
+		}
+	}
+	
+	actions = read_actions();
+	
 	/* detect the audio device */
 	if (find_hwdep_device(&card, &dev)) {
 		error("Can't find device. Bail");
 		return 1;
 	}
 
-	printf("Device is %d:%d\n", card, dev);
+	if(verbose)
+		printf("Device is %d:%d\n", card, dev);
 
 	sprintf(hwdep_name, "hw:%i,%i", card, dev);
 
@@ -358,21 +503,22 @@
 		return -1;
 	}
 
-	if (argc > 3) {
-		printf("Clearing fd\n");
+	if (clear) {
+		if(verbose)
+			printf("Clearing fd\n");
 		bt_sco_set_fd(handle, 1);
 		return 1;
 	}
 
 	/* find bdaddr */
-	switch (argc) {
-	case 2:
-		str2ba(argv[1], &bdaddr);
+	switch (argc - optind) {
+	case 1:
+		str2ba(argv[optind], &bdaddr);
 		channel = detect_channel(&bdaddr);
 		break;
-	case 3:
-		str2ba(argv[1], &bdaddr);
-		channel = atoi(argv[2]);
+	case 2:
+		str2ba(argv[optind], &bdaddr);
+		channel = atoi(argv[optind + 1]);
 		break;
 	default:
 		usage();
@@ -384,7 +530,8 @@
 	dd = hci_open_dev(0);
 	hci_read_voice_setting(dd, &vs, 1000);
 	vs = htobs(vs);
-	fprintf(stderr, "Voice setting: 0x%04x\n", vs);
+	if(verbose)
+		printf("Voice setting: 0x%04x\n", vs);
 	close(dd);
 	/*
 	   MU_LAW
@@ -396,7 +543,7 @@
 
 	// 16bit
 	if (vs != 0x060) {
-		fprintf(stderr, "The voice setting must be 0x060\n");
+		error("The voice setting must be 0x060\n");
 		return -1;
 	}
 
@@ -406,6 +553,12 @@
 	sa.sa_handler = sig_term;
 	sigaction(SIGTERM, &sa, NULL);
 	sigaction(SIGINT, &sa, NULL);
+	
+	sa.sa_handler = sig_ring;
+	sigaction(SIGUSR1, &sa, NULL);
+
+	sa.sa_handler = sig_hup;
+	sigaction(SIGHUP, &sa, NULL);
 
 	sa.sa_handler = SIG_IGN;
 	sigaction(SIGCHLD, &sa, NULL);
@@ -417,7 +570,8 @@
 		return -1;
 	}
 
-	fprintf(stderr, "RFCOMM channel %i connected\n", channel);
+	if(verbose)
+		printf("RFCOMM channel %i connected\n", channel);
 
 	i = 0;
 
@@ -437,151 +591,204 @@
 
 	last_volumes[0] = last_volumes[1] = 0;
 
+	snd_hwdep_ioctl(handle, SNDRV_BT_SCO_IOCTL_REQ_INFO, NULL);
+	if(snd_hwdep_read(handle, &infobuf, sizeof(infobuf)) < 0) {
+		perror("read info");
+		exit(1);
+	}
+	dr_usage = infobuf.playback_count || infobuf.capture_count;
+	force_sco = -1;
+	
+	if (fork)
+		daemon(0, 0);
+	
 	/* we are not yet connected */
 	sco_mode = NOT_CONNECTED;
 	sd = -1;
 	while (!terminate) {
 		/*printf("outer loop\n"); */
-		opdone = 0;
-
-		if (poll(pfds, nfds, 1000) > 0) {
+		
+		ret = poll(pfds, nfds, -1);
+		if ((ret < 0) && (errno != EINTR)) {
+			perror("poll");
+			sleep(1); /* Don't steal the CPU in case of non-transient errors. */
+		} else if (ret > 0) {
 			short revents;
 
 			/*printf("inner loop\n"); */
 			/* Volume polling (sound card) */
 			if (!snd_hwdep_poll_descriptors_revents
-			    (handle, &pfds[nfds - 1], 1, &revents)
-			    && revents & POLLIN) {
-				int len;
-
-				len =
-				    snd_hwdep_read(handle, volumes,
-						   sizeof(volumes));
-				if (len == sizeof(volumes)) {
-					printf
-					    ("speaker volume: %d mic volume: %d\n",
-					     volumes[0], volumes[1]);
-					if (volumes[0] != last_volumes[0]) {
-						sprintf(line, "AT+VGS=%d\r",
-							volumes[0]);
-						write(rd, line, strlen(line));
-					}
-					if (volumes[1] != last_volumes[1]) {
-						sprintf(line, "AT+VGM=%d\r",
-							volumes[1]);
-						write(rd, line, strlen(line));
+			    (handle, &pfds[nfds - 1], 1, &revents)) {
+				if (revents & POLLIN) {
+					int len;
+					
+					len = snd_hwdep_read(handle, &infobuf, sizeof(infobuf));
+					if (len == sizeof(infobuf)) {
+						if(verbose)
+							printf ("speaker volume: %d mic volume: %d\n",
+								infobuf.mixer_volume[0], infobuf.mixer_volume[1]);
+						if (infobuf.mixer_volume[0] != last_volumes[0]) {
+							sprintf(line, "AT+VGS=%d\r",
+								infobuf.mixer_volume[0]);
+							write(rd, line, strlen(line));
+						}
+						if (infobuf.mixer_volume[1] != last_volumes[1]) {
+							sprintf(line, "AT+VGM=%d\r",
+								infobuf.mixer_volume[1]);
+							write(rd, line, strlen(line));
+						}
+						memcpy(last_volumes, infobuf.mixer_volume,
+						       sizeof(infobuf.mixer_volume));
+						dr_usage = infobuf.playback_count || infobuf.capture_count;
 					}
-					memcpy(last_volumes, volumes,
-					       sizeof(volumes));
-					opdone = 1;
 				}
 			}
+			
 			// control transmission events for volume and channel control
 			if (pfds[0].revents & POLLIN) {
 				memset(buf, 0, sizeof(buf));
 				rlen = read(rd, buf, sizeof(buf) - 1);
 				if (rlen > 0) {
-					fprintf(stderr, "recieved %s\n", buf);
+					struct action *cur;
+					
+					if(verbose)
+						printf("recieved %s\n", buf);
 					/* tell them we recieved */
 					wlen = write(rd, "\r\nOK\r\n", 6);
 
-					if ( strstr(buf, "AT+CKPD=200")
-					    || strstr(buf, "AT+CHUP")
-					    || strstr(buf, "AT+CIND=?")) {
-						/* mini state machine: handle connect/disconnect */
-						switch (sco_mode) {
-						case NOT_CONNECTED:
-							fprintf(stderr,
-								"opened hwdep\n");
-							/* connect sco stream */
-							if ((sd =
-							     sco_connect(&local,
-									 &bdaddr,
-									 &sco_handle,
-									 &sco_mtu))
-							    < 0) {
-
-								perror
-								    ("Can't connect SCO audio channel\n");
+					for(cur = actions; cur != NULL; cur = cur->next) {
+						regmatch_t matches[10];
+						char *cmdbuf, *args;
+						int match;
+						
+						if(regexec(&cur->regex, buf, 10, matches, 0))
+							continue;
+						cmdbuf = strdup(cur->cmd);
+						if((args = strchr(cmdbuf, ' ')) != NULL)
+							*(args++) = 0;
+						if(!strcmp(cmdbuf, "system")) {
+							for(i = 0; args[i]; i++) {
+								if((args[i] == '\\') && (args[i + 1] >= '0') && (args[i + 1] <= '9')) {
+									match = args[i + 1] - '0';
+									if(matches[match].rm_so == -1)
+										continue;
+									args = realloc(args, strlen(args) + matches[match].rm_eo - matches[match].rm_so - 1);
+									memmove(args + i + matches[match].rm_eo - matches[match].rm_so, args + i + 2, strlen(args) - i - 1);
+									memmove(args + i, buf + matches[match].rm_so, matches[match].rm_eo - matches[match].rm_so);
+								}
+							}
+							if(verbose)
+								printf("running %s\n", args);
+							system(args);
+						} else if(!strcmp(cmdbuf, "sco-toggle")) {
+							int target;
+							char *p;
+							
+							target = 1;
+							p = NULL;
+							if(args != NULL) {
+								if((p = strchr(args, ' ')) != NULL)
+									*(p++) = 0;
+								if(!strcmp(args, "on"))
+									target = 1;
+								else if(!strcmp(args, "off"))
+									target = 0;
+								else if(!strcmp(args, "none"))
+									target = -1;
+							}
+							if(force_sco == target) {
+								force_sco = -1;
+								if(p != NULL) {
+									if(!strcmp(p, "on"))
+										force_sco = 1;
+									else if(!strcmp(p, "off"))
+										force_sco = 0;
+									else if(!strcmp(p, "none"))
+										force_sco = -1;
+								}
 							} else {
-								fprintf(stderr,
-									"connected SCO channel\n");
-								//      write(rd, "RING\r\n", 6);
-								printf
-								    ("Setting sco fd\n");
-								bt_sco_set_fd
-								    (handle,
-								     sd);
-
-								printf
-								    ("Done setting sco fd\n");
-								sco_mode =
-								    CONNECTED;
+								force_sco = target;
+							}
+						} else if(!strcmp(cmdbuf, "sco-force")) {
+							if(args != NULL) {
+								if(!strcmp(args, "on"))
+									force_sco = 1;
+								else if(!strcmp(args, "off"))
+									force_sco = 0;
+								else if(!strcmp(args, "none"))
+									force_sco = -1;
 							}
-
-							opdone = 1;
-							break;
-						case CONNECTED:
-							/* close bt_sco audio handle */
-							bt_sco_set_fd(handle,
-								      -1);
-							/* disconnect SCO stream */
-							close(sd);
-							fprintf(stderr,
-								"disconnected SCO channel\n");
-
-							sco_mode =
-							    NOT_CONNECTED;
-
-							opdone = 1;
-							break;
 						}
+						free(cmdbuf);
 					}
-
+					
 					if (sscanf
 					    (buf, "AT+VGS=%d",
-					     &volumes[0]) == 1) {
-						fprintf(stderr,
-							"Sending up speaker change %d\n",
-							volumes[0]);
+					     &infobuf.mixer_volume[0]) == 1) {
+						if(verbose)
+							printf("Sending up speaker change %d\n", infobuf.mixer_volume[0]);
 						snd_hwdep_write(handle,
-								volumes,
+								infobuf.mixer_volume,
 								sizeof
-								(volumes));
-						opdone = 1;
+								(infobuf.mixer_volume));
 					}
 					if (sscanf
 					    (buf, "AT+VGM=%d",
-					     &volumes[1]) == 1) {
-						fprintf(stderr,
-							"Sending up microphone change %d\n",
-							volumes[1]);
+					     &infobuf.mixer_volume[1]) == 1) {
+						if(verbose)
+							printf("Sending up microphone change %d\n", infobuf.mixer_volume[1]);
 						snd_hwdep_write(handle,
-								volumes,
+								infobuf.mixer_volume,
 								sizeof
-								(volumes));
-						opdone = 1;
+								(infobuf.mixer_volume));
 
 					}
 				}
 			}
 
-			/* Just for testing; handled by kernel driver */
-
-			if (0 && FD_ISSET(sd, &rfds)) {
-				memset(buf, 0, sizeof(buf));
-				rlen = read(sd, buf, sizeof(buf));
-				write(sd, buf, rlen);
-
-				i++;
+		}
 
-				if (i % 15 == 0) {
-					printf("rlen: %d\n", rlen);
-				}
+		if(((dr_usage && (force_sco != 0)) || (force_sco == 1)) && (sco_mode == NOT_CONNECTED)) {
+			if(verbose)
+				printf("driver is in use\n");
+			/* connect sco stream */
+			if ((sd = sco_connect(&local, &bdaddr, &sco_handle, &sco_mtu)) < 0) {
+				perror ("Can't connect SCO audio channel\n");
+			} else {
+				if(verbose)
+					printf("connected SCO channel\n");
+				bt_sco_set_fd (handle, sd);
+				
+				if(verbose)
+					printf ("Done setting sco fd\n");
+				sco_mode = CONNECTED;
 			}
+			
+		}
+		if(((!dr_usage && (force_sco != 1)) || (force_sco == 0)) && (sco_mode == CONNECTED)) {
+			if(verbose)
+				printf("driver is not in use\n");
+			/* close bt_sco audio handle */
+			bt_sco_set_fd(handle, -1);
+			/* disconnect SCO stream */
+			close(sd);
+			if(verbose)
+				printf("disconnected SCO channel\n");
+			
+			sco_mode = NOT_CONNECTED;
+			
+		}
+		
+		if (ring) {
+			write(rd, "\r\nRING\r\n", 8);
+			ring = 0;
+		}
+		
+		if (hupped) {
+			free_actions(actions);
+			actions = read_actions();
+			hupped = 0;
 		}
-		if (!opdone)
-			sleep(1);
 	}
 
 	if (sco_mode == CONNECTED) {
Index: kernel/btsco.c
===================================================================
RCS file: /cvsroot/bluetooth-alsa/btsco/kernel/btsco.c,v
retrieving revision 1.4
diff -u -r1.4 btsco.c
--- kernel/btsco.c	27 Nov 2004 03:07:09 -0000	1.4
+++ kernel/btsco.c	14 Mar 2005 14:55:27 -0000
@@ -54,6 +54,7 @@
 #endif
 
 #define SNDRV_BT_SCO_IOCTL_SET_SCO_SOCKET _IOW ('H', 0x10, int)
+#define SNDRV_BT_SCO_IOCTL_REQ_INFO _IO ('H', 0x11)
 
 MODULE_AUTHOR("Jonathan Paisley <jp@dcs.gla.ac.uk>");
 MODULE_DESCRIPTION("Bluetooth SCO Headset Soundcard");
@@ -83,12 +84,20 @@
 
 struct snd_card_bt_sco_pcm;
 
+typedef struct snd_card_bt_sco_info {
+	int mixer_volume[MIXER_ADDR_LAST + 1];
+	int playback_count, capture_count;
+} snd_card_bt_sco_info_t;
+
 typedef struct snd_card_bt_sco {
 	snd_card_t *card;
 	spinlock_t mixer_lock;
 	int mixer_volume[MIXER_ADDR_LAST + 1];
 	snd_kcontrol_t *mixer_controls[MIXER_ADDR_LAST + 2];	/* also loopback */
 	volatile int loopback;
+	atomic_t playback_count, capture_count;
+	volatile int count_changed;
+	spinlock_t count_changed_lock;
 
 	spinlock_t mixer_changed_lock;
 	volatile int mixer_changed;
@@ -322,6 +331,7 @@
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	snd_card_bt_sco_pcm_t *bspcm;
+	snd_card_bt_sco_t *bt_sco = snd_pcm_substream_chip(substream);
 
 	dprintk("playback_open\n");
 
@@ -339,6 +349,13 @@
 	runtime->private_data = bspcm;
 	runtime->private_free = snd_card_bt_sco_runtime_free;
 	runtime->hw = snd_card_bt_sco_playback;
+
+	atomic_inc(&bt_sco->playback_count);
+	spin_lock_irq(&bt_sco->count_changed_lock);
+	bt_sco->count_changed = 1;
+	spin_unlock_irq(&bt_sco->count_changed_lock);
+	wake_up(&bt_sco->hwdep_wait);
+
 	return 0;
 }
 
@@ -346,6 +363,7 @@
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	snd_card_bt_sco_pcm_t *bspcm;
+	snd_card_bt_sco_t *bt_sco = snd_pcm_substream_chip(substream);
 
 	dprintk("capture_open\n");
 
@@ -364,6 +382,13 @@
 	runtime->private_data = bspcm;
 	runtime->private_free = snd_card_bt_sco_runtime_free;
 	runtime->hw = snd_card_bt_sco_capture;
+
+	atomic_inc(&bt_sco->capture_count);
+	spin_lock_irq(&bt_sco->count_changed_lock);
+	bt_sco->count_changed = 1;
+	spin_unlock_irq(&bt_sco->count_changed_lock);
+	wake_up(&bt_sco->hwdep_wait);
+
 	return 0;
 }
 
@@ -379,6 +404,12 @@
 	down(&bt_sco->playback_sem);
 	up(&bt_sco->playback_sem);
 
+	atomic_dec(&bt_sco->playback_count);
+	spin_lock_irq(&bt_sco->count_changed_lock);
+	bt_sco->count_changed = 1;
+	spin_unlock_irq(&bt_sco->count_changed_lock);
+	wake_up(&bt_sco->hwdep_wait);
+
 	snd_free_pages(runtime->dma_area, runtime->dma_bytes);
 	return 0;
 }
@@ -396,6 +427,12 @@
 	down(&bt_sco->capture_sem);
 	up(&bt_sco->capture_sem);
 
+	atomic_dec(&bt_sco->capture_count);
+	spin_lock_irq(&bt_sco->count_changed_lock);
+	bt_sco->count_changed = 1;
+	spin_unlock_irq(&bt_sco->count_changed_lock);
+	wake_up(&bt_sco->hwdep_wait);
+
 	snd_free_pages(runtime->dma_area, runtime->dma_bytes);
 	return 0;
 }
@@ -580,7 +617,7 @@
 {
 	snd_card_bt_sco_t *bt_sco = hw->card->private_data;
 	struct socket *sock;
-	int err = -EINVAL;
+	int err = -ENOTTY;
 	int fd = arg;
 
 	switch (cmd) {
@@ -619,6 +656,12 @@
 		}
 		up(&bt_sco->sock_sem);
 		break;
+	case SNDRV_BT_SCO_IOCTL_REQ_INFO:
+		spin_lock_irq(&bt_sco->count_changed_lock);
+		bt_sco->count_changed = 1;
+		spin_unlock_irq(&bt_sco->count_changed_lock);
+		wake_up(&bt_sco->hwdep_wait);
+		break;
 	}
 	return err;
 }
@@ -664,19 +707,28 @@
 	snd_card_bt_sco_t *bt_sco = hw->card->private_data;
 	DECLARE_WAITQUEUE(wait, current);
 	ssize_t retval;
+	int changed;
+	snd_card_bt_sco_info_t infobuf;
 
-	if (count != sizeof(bt_sco->mixer_volume))
+	if (count < sizeof(bt_sco->mixer_volume))
 		return -EINVAL;
 
 	add_wait_queue(&bt_sco->hwdep_wait, &wait);
 	current->state = TASK_INTERRUPTIBLE;
 	do {
-		int changed;
+		changed = 0;
 		spin_lock_irq(&bt_sco->mixer_changed_lock);
-		changed = bt_sco->mixer_changed;
+		if(bt_sco->mixer_changed)
+			changed = 1;
 		bt_sco->mixer_changed = 0;
 		spin_unlock_irq(&bt_sco->mixer_changed_lock);
 
+		spin_lock_irq(&bt_sco->count_changed_lock);
+		if(bt_sco->count_changed)
+			changed = 1;
+		bt_sco->count_changed = 0;
+		spin_unlock_irq(&bt_sco->count_changed_lock);
+
 		if (changed != 0)
 			break;
 
@@ -686,11 +738,16 @@
 		}
 		schedule();
 	} while (1);
+	
+	memcpy(infobuf.mixer_volume, bt_sco->mixer_volume, sizeof(infobuf.mixer_volume));
+	infobuf.playback_count = atomic_read(&bt_sco->playback_count);
+	infobuf.capture_count = atomic_read(&bt_sco->capture_count);
+	
 	if (copy_to_user
-	    (buf, bt_sco->mixer_volume, sizeof(bt_sco->mixer_volume)))
+	    (buf, &infobuf, sizeof(infobuf)))
 		retval = -EFAULT;
 	else
-		retval = sizeof(bt_sco->mixer_volume);
+		retval = sizeof(infobuf);
 
       out:
 	current->state = TASK_RUNNING;
@@ -702,17 +759,22 @@
 				     struct file *file, poll_table * wait)
 {
 	snd_card_bt_sco_t *bt_sco = hw->card->private_data;
-	int changed;
+	int ret;
 
 	poll_wait(file, &bt_sco->hwdep_wait, wait);
 
+	ret = 0;
 	spin_lock_irq(&bt_sco->mixer_changed_lock);
-	changed = bt_sco->mixer_changed;
+	if(bt_sco->mixer_changed)
+		ret |= POLLIN | POLLRDNORM;
 	spin_unlock_irq(&bt_sco->mixer_changed_lock);
 
-	if (changed != 0)
-		return POLLIN | POLLRDNORM;
-	return 0;
+	spin_lock_irq(&bt_sco->count_changed_lock);
+	if(bt_sco->count_changed)
+		ret |= POLLIN | POLLRDNORM;
+	spin_unlock_irq(&bt_sco->count_changed_lock);
+
+	return ret;
 }
 
 static int snd_card_bt_sco_thread(void *data)
@@ -919,6 +981,7 @@
 	init_waitqueue_head(&bt_sco->wait);
 	init_waitqueue_head(&bt_sco->hwdep_wait);
 	spin_lock_init(&bt_sco->mixer_changed_lock);
+	spin_lock_init(&bt_sco->count_changed_lock);
 
 	/* These clone flags copied from some other driver.
 	   Not sure that they're really correct... */
