NetBSD Problem Report #48090

From www@NetBSD.org  Sun Jul 28 10:58:25 2013
Return-Path: <www@NetBSD.org>
Received: from mail.netbsd.org (mail.netbsd.org [149.20.53.66])
	(using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
	(Client CN "mail.NetBSD.org", Issuer "Postmaster NetBSD.org" (verified OK))
	by mollari.NetBSD.org (Postfix) with ESMTPS id CBACC70D08
	for <gnats-bugs@gnats.NetBSD.org>; Sun, 28 Jul 2013 10:58:25 +0000 (UTC)
Message-Id: <20130728105820.B750370D0A@mollari.NetBSD.org>
Date: Sun, 28 Jul 2013 10:58:20 +0000 (UTC)
From: nathanialsloss@yahoo.com.au
Reply-To: nathanialsloss@yahoo.com.au
To: gnats-bugs@NetBSD.org
Subject: bthset to use pad devices and support 8 bit sco audio
X-Send-Pr-Version: www-1.0

>Number:         48090
>Category:       bin
>Synopsis:       bthset to use pad devices and support 8 bit sco audio
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    plunky
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sun Jul 28 11:00:00 +0000 2013
>Last-Modified:  Mon Aug 26 01:45:00 +0000 2013
>Originator:     Nat Sloss
>Release:        NetBSD 5.0.1
>Organization:
>Environment:
NetBSD beast 5.0.1 NetBSD 5.0.1 (LOCKDEBUG) #4: Wed Jul 17 22:24:41 EST 2013  build@beast:/home/build/NetBSD-5.0.1_source_tree/usr/src/sys/arch/i386/compile/obj/LOCKDEBUG i386
>Description:
Currently btsco audio is limited to 16 bit PCM and does not support 8 bit audio.  Also btsco packets are sent via a kernel device and the setup is a little complicated (first connecting btsco device and then, opening a control channel with bthset).

So I have modified bthset to use the pad(4) audio device and handle audio directly no need for btattach(8).

So to use this patch apply my patch PR# kern/46545 [serious/medium]:
        pad(4):Support for fullduplex playback/recording polling and nonblocking mode

this will allow data to be an input to a pad device for microphone input and support for polling (necessary for the code that transmitts the audio).

Then apply this patch for bthset.c

setup btsco voice and sco mtu

sysctl -w hw.ubt0.config=1            - 8 bit one channel.
btconfig ubt0 voice 0x100 scomtu 24 up   -8 bit mulaw 24 byte mtu works with my head set.

Then finally issue
bthset -d ubt0 -a (bluetooth device name) -m /dev/mixer1 -f /dev/pad0
where mixer1 is the pad audio mixer.

Then it is possible to use recording with /dev/audio1 using standard audio tools.


>How-To-Repeat:
This is an alternative to the current btsco setup.

>Fix:
Index: src/usr.bin/bthset/bthset.c
===================================================================
RCS file: /cvsroot/src/usr.bin/bthset/bthset.c,v
retrieving revision 1.8
diff -u -r1.8 bthset.c
--- src/usr.bin/bthset/bthset.c	15 Mar 2012 02:02:23 -0000	1.8
+++ src/usr.bin/bthset/bthset.c	28 Jul 2013 10:36:04 -0000
@@ -38,6 +38,8 @@
 #include <sys/types.h>
 #include <sys/audioio.h>
 #include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/select.h>
 #include <sys/time.h>
 #include <sys/uio.h>

@@ -46,6 +48,7 @@
 #include <err.h>
 #include <event.h>
 #include <fcntl.h>
+#include <poll.h>
 #include <sdp.h>
 #include <signal.h>
 #include <stdarg.h>
@@ -58,6 +61,8 @@
 #include <dev/bluetooth/btdev.h>
 #include <dev/bluetooth/btsco.h>

+#include <netbt/hci.h>
+#include <netbt/sco.h>
 #include <netbt/rfcomm.h>

 #define RING_INTERVAL	5	/* seconds */
@@ -66,11 +71,14 @@

 static void do_signal(int, short, void *);
 static void do_ring(int, short, void *);
+static void do_audio(int, short, void *);
+static void do_audio_connect(int, short, void *);
 static void do_mixer(int, short, void *);
 static void do_rfcomm(int, short, void *);
 static void do_server(int, short, void *);
 static int send_rfcomm(const char *, ...) __printflike(1, 2);

+static int init_audio(struct btsco_info *, const char *);
 static int init_mixer(struct btsco_info *, const char *);
 static int init_rfcomm(struct btsco_info *);
 static int init_server(struct btsco_info *, int);
@@ -78,9 +86,25 @@
 static void remove_pid(void);
 static int write_pid(void);

+static void convert441002chto80001ch(uint8_t *, int, uint8_t *, int);
+static void convert80001chto441002ch(uint8_t *, int, uint8_t *, int);
+static void convertlinear16toalaw(uint8_t *, int, uint8_t *, int);
+static void convertlinear16tomulaw(uint8_t *, int, uint8_t *, int);
+static void convertmulawtolinear16(uint8_t *, int, uint8_t *, int);
+static void convertalawtolinear16(uint8_t *, int, uint8_t *, int);
+static void convertlinear16tolinear8(uint8_t *, int, uint8_t *, int);
+static void convertlinear8tolinear16(uint8_t *, int, uint8_t *, int);
+
+static int service_search(const bdaddr_t *, const bdaddr_t *, uint16_t,
+    uintmax_t *, uintmax_t *);
+
+static void hci_req(int, uint16_t, uint8_t , void *, size_t, void *, size_t);
+static int btsco_read_voice_setting(struct btsco_info *, uint32_t *);
+
 static struct event sigint_ev;		/* bye bye */
 static struct event sigusr1_ev;	/* start ringing */
 static struct event sigusr2_ev;	/* stop ringing */
+static struct event audio_ev;		/* audio event */
 static struct event mixer_ev;		/* mixer changed */
 static struct event rfcomm_ev;		/* headset speaks */
 static struct event server_ev;		/* headset connecting */
@@ -93,7 +117,18 @@
 static int mx;			/* mixer fd */
 static int rf;			/* rfcomm connection fd */
 static int ag;			/* rfcomm gateway fd */
-static sdp_session_t ss;	/* SDP server session */
+static int ad;			/* audio SCO fd */
+static int ab;			/* audio SCO fd */
+static int readfrom;		/* file to read from */
+static int writeto;		/* file to write to */
+static void *ss;		/* sdp handle */
+static fd_set rfds;		/* read rdset */
+static struct pollfd pollfd[2];	/* poll fds */
+static int testmode = 0;
+static uint32_t voicesetting = 0x60;
+
+static uint8_t *mtubuf, *readbuf, *tmpbuf;
+static size_t mtubufsize, readbufsize, tmpbufsize;

 static char *command;		/* answer command */
 static char *pidfile;		/* PID file name */
@@ -148,6 +183,7 @@
 {
 	struct btsco_info	info;
 	const char		*mixer;
+	const char		*audiofile;
 	int			ch, channel;

 	ag = rf = -1;
@@ -156,15 +192,39 @@
 	pidfile = getenv("BTHSET_PIDFILE");
 	command = getenv("BTHSET_COMMAND");
 	mixer = getenv("BTHSET_MIXER");
+	audiofile = getenv("BTHSET_AUDIOFILE");
+	bdaddr_copy(&info.laddr, BDADDR_ANY);
 	if (mixer == NULL)
 		mixer = "/dev/mixer";

-	while ((ch = getopt(ac, av, "hc:m:p:s:v")) != EOF) {
+	while ((ch = getopt(ac, av, "hc:m:p:s:a:d:f:vt")) != EOF) {
 		switch (ch) {
+		case 'a': /* remote address */
+			if (!bt_aton(optarg, &info.raddr)) {
+				struct hostent  *he = NULL;
+
+				if ((he = bt_gethostbyname(optarg)) == NULL)
+					errx(EXIT_FAILURE, "%s: %s",
+						optarg, hstrerror(h_errno));
+
+				bdaddr_copy(&info.raddr, (bdaddr_t *)he->h_addr);
+			}
+			break;
+
 		case 'c':
 			command = optarg;
 			break;

+		case 'd': /* local device address */
+			if (!bt_devaddr(optarg, &info.laddr))
+				err(EXIT_FAILURE, "%s", optarg);
+
+			break;
+
+		case 'f':
+			audiofile = optarg;
+			break;
+
 		case 'm':
 			mixer = optarg;
 			break;
@@ -177,6 +237,10 @@
 			channel = atoi(optarg);
 			break;

+		case 't':
+			testmode = 1;
+			break;
+
 		case 'v':
 			verbose = 1;
 			break;
@@ -190,6 +254,9 @@
 	if (mixer == NULL)
 		usage();

+	if (audiofile == NULL)
+		usage();
+
 	if ((channel < RFCOMM_CHANNEL_MIN || channel > RFCOMM_CHANNEL_MAX)
 	    && channel != 0)
 		usage();
@@ -198,6 +265,7 @@
 		err(EXIT_FAILURE, "%s", pidfile);

 	event_init();
+	event_priority_init(20);

 	ringing = 0;
 	evtimer_set(&ring_ev, do_ring, NULL);
@@ -223,18 +291,28 @@
 	if (channel && init_server(&info, channel) < 0)
 		err(EXIT_FAILURE, "%d", channel);

+	if (channel == 0  && init_audio(&info, audiofile) < 0)
+		err(EXIT_FAILURE, "%s", bt_ntoa(&info.raddr, NULL));
+
 	if (verbose) {
-		printf("Headset Info:\n");
-		printf("\tmixer: %s\n", mixer);
-		printf("\tladdr: %s\n", bt_ntoa(&info.laddr, NULL));
-		printf("\traddr: %s\n", bt_ntoa(&info.raddr, NULL));
-		printf("\tchannel: %d\n", info.channel);
-		printf("\tvgs.dev: %d, vgm.dev: %d\n", vgs.dev, vgm.dev);
-		if (channel) printf("\tserver channel: %d\n", channel);
+		fprintf(stderr, "Headset Info:\n");
+		fprintf(stderr, "\tmixer: %s\n", mixer);
+		fprintf(stderr, "\tladdr: %s\n", bt_ntoa(&info.laddr, NULL));
+		fprintf(stderr, "\traddr: %s\n", bt_ntoa(&info.raddr, NULL));
+		fprintf(stderr, "\tchannel: %d\n", info.channel);
+		fprintf(stderr, "\tvgs.dev: %d, vgm.dev: %d\n", vgs.dev, vgm.dev);
+		if (channel) fprintf(stderr, "\tserver channel: %d\n", channel);
 	}

 	event_dispatch();

+	if (mtubuf != NULL)
+		free(mtubuf);
+	if (tmpbuf != NULL)
+		free(tmpbuf);
+	if (readbuf != NULL)
+		free(readbuf);
+
 	err(EXIT_FAILURE, "event_dispatch");
 }

@@ -243,10 +321,14 @@
 {

 	fprintf(stderr,
-		"usage: %s [-hv] [-c command] [-m mixer] [-p file] [-s channel]\n"
+		"usage: %s [-hvt] -a address -d device -f paddevice [-c command] [-m mixer] [-p file] [-s channel]\n"
 		"Where:\n"
 		"\t-h           display this message\n"
 		"\t-v           verbose output\n"
+		"\t-t		testmode (audio loopback)\n"
+		"\t-a address   remote device address\n"
+		"\t-d device    local device address\n"
+		"\t-f paddevice paddevice to use (otherwise stdin/stdout)\n"
 		"\t-c command   command to execute on answer\n"
 		"\t-m mixer     mixer path\n"
 		"\t-p file      write PID to file\n"
@@ -319,6 +401,215 @@

 		send_rfcomm("+VGM=%d", level);
 	}
+
+}
+
+/*
+ * Audio SCO socket event.
+ */
+static void
+do_audio_connect(int fd, short ev, void *arg)
+{
+	struct btsco_info *info = arg;
+	struct sockaddr_bt addr;
+	struct timeval tv;
+	uint16_t scomtu;
+	socklen_t scomtu_size = sizeof(scomtu);
+
+	ad = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+	if (ad < 0) {
+		fprintf(stderr, "audio socket failed\n");
+		close(ad);
+		errx(EXIT_FAILURE, NULL);
+		return;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.bt_len = sizeof(addr);
+	addr.bt_family = AF_BLUETOOTH;
+	addr.bt_channel = info->channel;
+	bdaddr_copy(&addr.bt_bdaddr, &info->laddr);
+
+	if (bind(ad, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		fprintf(stderr, "audio socket bind failed\n");
+		close(ad);
+		errx(EXIT_FAILURE, NULL);
+		return;
+	}
+
+	bdaddr_copy(&addr.bt_bdaddr, &info->raddr);
+
+	if (connect(ad, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		warn("%s", strerror(errno));
+		fprintf(stderr, "audio socket connect failed\n");
+		close(ad);
+		errx(EXIT_FAILURE, NULL);
+		return;
+	}
+
+ 	btsco_read_voice_setting(info, &voicesetting);
+	getsockopt(ad, BTPROTO_SCO, SO_SCO_MTU, &scomtu, &scomtu_size);
+	mtubuf = malloc(scomtu);
+	mtubufsize = scomtu;
+	tmpbufsize = mtubufsize * 2;
+	tmpbuf = malloc(tmpbufsize);
+	if (voicesetting == 0x60)
+		readbufsize = mtubufsize * 44100 / 8000 * 2;
+	else
+		readbufsize = tmpbufsize * 44100 / 8000 * 2;
+	readbuf = malloc(readbufsize);
+
+	if (fcntl(ad, F_SETFL, O_NONBLOCK) < 0) {
+		errx(EXIT_FAILURE, NULL);
+		return;
+	}
+
+	if (fcntl(readfrom, F_SETFL, O_NONBLOCK) < 0) {
+		errx(EXIT_FAILURE, NULL);
+		return;
+	}
+
+	tv.tv_sec = tv.tv_usec = 0;
+
+	memset(&pollfd[0], 0, sizeof(pollfd[0]));
+	pollfd[0].fd = readfrom;
+	pollfd[0].events = POLLRDNORM;
+
+	memset(&pollfd[1], 0, sizeof(pollfd[1]));
+	pollfd[1].fd = writeto;
+	pollfd[1].events = POLLWRNORM;
+
+	FD_ZERO(&rfds);
+	FD_SET(readfrom, &rfds);
+
+	event_set(&audio_ev, ad, EV_WRITE | EV_READ | EV_PERSIST,
+	    do_audio, info);
+	if (event_add(&audio_ev, NULL) < 0) {
+		fprintf(stderr, "audio event set failed\n");
+		errx(EXIT_FAILURE, NULL);
+		return;
+	}
+ 
+}
+
+/*
+ * Audio SCO socket event.
+ */
+static void
+do_audio(int fd, short ev, void *arg)
+{
+	struct btsco_info *info = arg;
+	struct sockaddr_bt addr;
+	struct timeval tv;
+
+	int selres, nr, tries;
+	unsigned int i, len;
+
+	FD_ZERO(&rfds);
+	FD_SET(readfrom, &rfds);
+
+	nr = 0;
+	tv.tv_sec = 0;
+	tv.tv_usec = 0;
+	memset(mtubuf, 0, mtubufsize);
+	memset(tmpbuf, 0, tmpbufsize);
+	memset(readbuf, 0, readbufsize);
+	for (i = 0;i < mtubufsize;i ++)
+		mtubuf[i] = 0xFF;
+
+	bdaddr_copy(&addr.bt_bdaddr, &info->raddr);
+	addr.bt_channel = info->channel;
+	addr.bt_family = AF_BLUETOOTH;
+
+	tries = 0;
+	while (tries < 3) {
+		len = recv(ad, mtubuf, mtubufsize, 0);
+		if (len == mtubufsize)
+			break;
+		if (errno == EAGAIN)
+			tries ++;
+		else if (len <= 0 &&  tries == 3) {
+			warn("%s", strerror(errno));
+			event_del(&audio_ev);
+			close(ad);
+			ad = -1;
+			return;
+		}
+	}
+
+	if (testmode == 0) {
+		if ((voicesetting & 0xf00) == 0x100) {
+			/* Mulaw */
+			convertmulawtolinear16(mtubuf, mtubufsize,
+			    tmpbuf, tmpbufsize);
+			convert80001chto441002ch(tmpbuf, tmpbufsize,
+		    	    readbuf, readbufsize);
+		} else if ((voicesetting & 0xf00) == 0x200) {
+			/* Alaw */
+			convertalawtolinear16(mtubuf, mtubufsize,
+			    tmpbuf, tmpbufsize);
+			convert80001chto441002ch(tmpbuf, tmpbufsize,
+		    	    readbuf, readbufsize);
+		} else if ((voicesetting & 0xff0) == 0x40) {
+			convertlinear8tolinear16(mtubuf, mtubufsize,
+			    tmpbuf, tmpbufsize);
+			convert80001chto441002ch(tmpbuf, tmpbufsize,
+		    	    readbuf, readbufsize);
+		} else {
+			convert80001chto441002ch(mtubuf, mtubufsize,
+		    	    readbuf, readbufsize);
+		}
+
+		selres = poll(&pollfd[1], 1, 0);
+
+		if (selres && !(pollfd[1].revents & (POLLERR | POLLHUP)))
+			nr = write(writeto, readbuf, readbufsize);
+
+		memset(readbuf, 0, readbufsize);
+		selres = poll(&pollfd[0], 1, 0);
+
+		if (selres && !(pollfd[0].revents & (POLLERR | POLLHUP)))
+			nr = read(readfrom, readbuf, readbufsize);
+
+		if ((voicesetting & 0xf00) == 0x100) {
+			/* Mulaw */
+			convert441002chto80001ch(readbuf, readbufsize,
+		    	    tmpbuf, tmpbufsize);
+			convertlinear16tomulaw(tmpbuf, tmpbufsize,
+			    mtubuf, mtubufsize);
+		} else if ((voicesetting & 0xf00) == 0x200) {
+			/* Alaw */
+			convert441002chto80001ch(readbuf, readbufsize,
+		    	    tmpbuf, tmpbufsize);
+			convertlinear16toalaw(tmpbuf, tmpbufsize,
+			    mtubuf, mtubufsize);
+		} else if ((voicesetting & 0xff0) == 0x40) {
+			convert441002chto80001ch(readbuf, readbufsize,
+		    	    tmpbuf, tmpbufsize);
+			convertlinear16tolinear8(tmpbuf, tmpbufsize,
+			    mtubuf, mtubufsize);
+		} else {
+			convert441002chto80001ch(readbuf, readbufsize,
+		    	    mtubuf, mtubufsize);
+		}
+	}
+
+	tries = 0;
+	while (tries < 3) {
+		len = send(ad, mtubuf, mtubufsize, 0);
+		if (len == mtubufsize)
+			break;
+		if (errno == EAGAIN)
+			tries ++;
+		else if (len <= 0 &&  tries == 3) {
+			warn("%s", strerror(errno));
+			event_del(&audio_ev);
+			close(ad);
+			ad = -1;
+			return;
+		}
+	}
+
 }

 /*
@@ -333,6 +624,7 @@
 	memset(buf, 0, sizeof(buf));
 	len = recv(rf, buf, sizeof(buf), 0);
 	if (len <= 0) {
+		warn("%s",strerror(errno));
 		if (ag < 0)
 			errx(EXIT_FAILURE, "Connection Lost");

@@ -344,12 +636,12 @@
 	}

 	if (verbose)
-		printf("> %.*s\n", len, buf);
+		fprintf(stderr, "> %.*s\n", len, buf);

 	if (len >= 7 && strncmp(buf, "AT+CKPD", 7) == 0) {
 		if (ringing && command != NULL) {
 			if (verbose)
-				printf("%% %s\n", command);
+				fprintf(stderr, "%% %s\n", command);

 			system(command);
 		}
@@ -456,21 +748,35 @@
 init_mixer(struct btsco_info *info, const char *mixer)
 {

+	mixer_devinfo_t dinfo;
+	int ndev, i;
+
 	mx = open(mixer, O_WRONLY, 0);
 	if (mx < 0)
 		return -1;

-	if (ioctl(mx, BTSCO_GETINFO, info) < 0)
-		return -1;
+	memset(&vgs, 0, sizeof(vgs));
+	memset(&vgm, 0, sizeof(vgm));
+
+	for(ndev = 0; ; ndev++) {
+		dinfo.index = ndev;
+		if (ioctl(mx, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
+			break;
+	}
+	for(i = 0;i < ndev ; i++) {
+		dinfo.index = i;
+		ioctl(mx, AUDIO_MIXER_DEVINFO, &dinfo);
+
+		if (strcmp(dinfo.label.name, "master") == 0)
+			vgs.dev=i;
+		if (strcmp(dinfo.label.name, "mic") == 0)
+			vgm.dev=i;
+	}

 	/* get initial vol settings */
-	memset(&vgs, 0, sizeof(vgs));
-	vgs.dev = info->vgs;
 	if (ioctl(mx, AUDIO_MIXER_READ, &vgs) < 0)
 		return -1;

-	memset(&vgm, 0, sizeof(vgm));
-	vgm.dev = info->vgm;
 	if (ioctl(mx, AUDIO_MIXER_READ, &vgm) < 0)
 		return -1;

@@ -486,12 +792,50 @@
 }

 /*
+ * Initialise audio SCO socket
+ */
+static int
+init_audio(struct btsco_info *info, const char *audiofile)
+{
+	struct timeval tv;
+
+	if (strcmp(audiofile, "-") == 0) {
+		readfrom = fileno(stdin);
+		writeto = fileno(stdout);
+	} else {
+		readfrom = open(audiofile, O_RDONLY);
+		writeto = open(audiofile, O_WRONLY);
+	}
+
+	if (readfrom < 0 || writeto < 0) {
+		errx(EXIT_FAILURE, NULL);
+		return -1;
+	}
+
+	fflush(stdin);
+	fflush(stdout);
+
+	tv.tv_sec = 6;
+	tv.tv_usec = 500;
+
+	event_once(ab, EV_TIMEOUT,
+	    do_audio_connect, info, &tv);
+ 
+	return 0;
+}
+
+/*
  * Initialise RFCOMM socket
  */
 static int
 init_rfcomm(struct btsco_info *info)
 {
 	struct sockaddr_bt addr;
+	uintmax_t psm, channel = 0;
+
+	if (service_search(&info->laddr, &info->raddr,
+	    SDP_SERVICE_CLASS_HEADSET, &psm, &channel) < 0)
+		return -1;

 	rf = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
 	if (rf < 0)
@@ -506,12 +850,14 @@
 		return -1;

 	bdaddr_copy(&addr.bt_bdaddr, &info->raddr);
-	addr.bt_channel = info->channel;

+	info->channel = channel;
+	addr.bt_channel = info->channel;
 	if (connect(rf, (struct sockaddr *)&addr, sizeof(addr)) < 0)
 		return -1;

 	event_set(&rfcomm_ev, rf, EV_READ | EV_PERSIST, do_rfcomm, NULL);
+
 	if (event_add(&rfcomm_ev, NULL) < 0)
 		return -1;

@@ -594,3 +940,602 @@

 	return atexit(remove_pid);
 }
+
+static int
+service_search(bdaddr_t const *laddr, bdaddr_t const *raddr,
+    uint16_t class, uintmax_t *psm, uintmax_t *channel)
+{
+	uint8_t		buffer[6];	/* SSP (3 bytes) + AIL (3 bytes) */
+	sdp_session_t	current_ss;
+	sdp_data_t	ail, ssp, rsp, rec, value, pdl, seq;
+	uint16_t	attr;
+	bool		rv;
+
+	seq.next = buffer;
+	seq.end = buffer + sizeof(buffer);
+
+	/*
+	 * build ServiceSearchPattern (3 bytes)
+	 */
+	ssp.next = seq.next;
+	sdp_put_uuid16(&seq, class);
+	ssp.end = seq.next;
+
+	/*
+	 * build AttributeIDList (3 bytes)
+	 */
+	ail.next = seq.next;
+	sdp_put_uint16(&seq, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
+	ail.end = seq.next;
+
+	current_ss = sdp_open(laddr, raddr);
+	if (current_ss == NULL)
+		return -1;
+
+	rv = sdp_service_search_attribute(current_ss, &ssp, &ail, &rsp);
+	if (!rv) {
+		sdp_close(current_ss);
+		return -1;
+	}
+
+	/*
+	 * The response will be a list of records that matched our
+	 * ServiceSearchPattern, where each record is a sequence
+	 * containing a single ProtocolDescriptorList attribute and
+	 * value
+	 *
+	 *	seq
+	 *	  uint16	ProtocolDescriptorList
+	 *	  value
+	 *	seq
+	 *	  uint16	ProtocolDescriptorList
+	 *	  value
+	 *
+	 * If the ProtocolDescriptorList describes a single stack,
+	 * the attribute value takes the form of a single Data Element
+	 * Sequence where each member is a protocol descriptor.
+	 *
+	 *	seq
+	 *	  list
+	 *
+	 * If it is possible for more than one kind of protocol
+	 * stack to be used to gain access to the service, the
+	 * ProtocolDescriptorList takes the form of a Data Element
+	 * Alternative where each member is a Data Element Sequence
+	 * describing an alternative protocol stack.
+	 *
+	 *	alt
+	 *	  seq
+	 *	    list
+	 *	  seq
+	 *	    list
+	 *
+	 * Each protocol stack description contains a sequence for each
+	 * protocol, where each sequence contains the protocol UUID as
+	 * the first element, and any ProtocolSpecificParameters. We are
+	 * interested in the L2CAP psm if provided, and the RFCOMM channel
+	 * number, stored as parameter#1 in each case.
+	 *
+	 *	seq
+	 *	  uuid		L2CAP
+	 *	  uint16	psm
+	 *	seq
+	 *	  uuid		RFCOMM
+	 *	  uint8		channel
+	 */
+
+	rv = false;
+	while (!rv && sdp_get_seq(&rsp, &rec)) {
+		if (!sdp_get_attr(&rec, &attr, &value)
+		    || attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST)
+			continue;
+
+		sdp_get_alt(&value, &value);	/* strip any alt container */
+		while (!rv && sdp_get_seq(&value, &pdl)) {
+			*psm = L2CAP_PSM_RFCOMM;
+			if (sdp_get_seq(&pdl, &seq)
+			    && sdp_match_uuid16(&seq, SDP_UUID_PROTOCOL_L2CAP)
+			    && (sdp_get_uint(&seq, psm) || true)
+			    && sdp_get_seq(&pdl, &seq)
+			    && sdp_match_uuid16(&seq, SDP_UUID_PROTOCOL_RFCOMM)
+			    && sdp_get_uint(&seq, channel))
+				rv = true;
+		}
+	}
+
+	sdp_close(current_ss);
+	if (rv)
+		return 0;
+	errno = ENOATTR;
+	return -1;
+}
+
+/*
+ * basic HCI cmd request function with argument return.
+ *
+ * Normally, this will return on COMMAND_STATUS or COMMAND_COMPLETE for the given
+ * opcode, but if event is given then it will ignore COMMAND_STATUS (unless error)
+ * and wait for the specified event.
+ *
+ * if rbuf/rlen is given, results will be copied into the result buffer for
+ * COMMAND_COMPLETE/event responses.
+ */
+static void
+hci_req(int hci_sock, uint16_t opcode, uint8_t event, void *cbuf, size_t clen, void *rbuf, size_t rlen)
+{
+	uint8_t msg[sizeof(hci_cmd_hdr_t) + HCI_CMD_PKT_SIZE];
+	hci_event_hdr_t *ep;
+	hci_cmd_hdr_t *cp;
+
+	cp = (hci_cmd_hdr_t *)msg;
+	cp->type = HCI_CMD_PKT;
+	cp->opcode = opcode = htole16(opcode);
+	cp->length = clen = MIN(clen, sizeof(msg) - sizeof(hci_cmd_hdr_t));
+
+	if (clen) memcpy((cp + 1), cbuf, clen);
+
+	if (send(hci_sock, msg, sizeof(hci_cmd_hdr_t) + clen, 0) < 0)
+		err(EXIT_FAILURE, "HCI Send");
+
+	ep = (hci_event_hdr_t *)msg;
+	for(;;) {
+		if (recv(hci_sock, msg, sizeof(msg), 0) < 0) {
+			if (errno == EAGAIN || errno == EINTR)
+				continue;
+
+			err(EXIT_FAILURE, "HCI Recv");
+		}
+
+		if (ep->event == HCI_EVENT_COMMAND_STATUS) {
+			hci_command_status_ep *cs;
+
+			cs = (hci_command_status_ep *)(ep + 1);
+			if (cs->opcode != opcode)
+				continue;
+
+			if (cs->status)
+				errx(EXIT_FAILURE,
+				    "HCI cmd (%4.4x) failed (status %d)",
+				    opcode, cs->status);
+
+			if (event == 0)
+				break;
+
+			continue;
+		}
+
+		if (ep->event == HCI_EVENT_COMMAND_COMPL) {
+			hci_command_compl_ep *cc;
+			uint8_t *ptr;
+
+			cc = (hci_command_compl_ep *)(ep + 1);
+			if (cc->opcode != opcode)
+				continue;
+
+			if (rbuf == NULL)
+				break;
+
+			ptr = (uint8_t *)(cc + 1);
+			if (*ptr)
+				errx(EXIT_FAILURE,
+				    "HCI cmd (%4.4x) failed (status %d)",
+				    opcode, *ptr);
+
+			memcpy(rbuf, ++ptr, rlen);
+			break;
+		}
+
+		if (ep->event == event) {
+			if (rbuf == NULL)
+				break;
+
+			memcpy(rbuf, (ep + 1), rlen);
+			break;
+		}
+	}
+}
+
+static int
+btsco_read_voice_setting(struct btsco_info *info, uint32_t *voice)
+{
+	struct sockaddr_bt sa_addr;
+	int hci_sock;
+	uint16_t rp;
+	uint16_t hcicmd;
+	int result_err;
+
+	result_err = 0;
+	sa_addr.bt_len = sizeof(struct sockaddr_bt);
+	sa_addr.bt_family = AF_BLUETOOTH;
+	bdaddr_copy(&sa_addr.bt_bdaddr, &info->laddr);
+
+	hci_sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+	if (result_err) {
+		goto voice_err;
+	}
+
+	result_err = bind(hci_sock, (struct sockaddr *)&sa_addr, sizeof(sa_addr));
+	if (result_err)
+		goto voice_err;
+
+	result_err = connect(hci_sock, (struct sockaddr *)&sa_addr, sizeof(sa_addr));
+
+	if (result_err)
+		goto voice_err;
+
+	hcicmd = HCI_CMD_READ_VOICE_SETTING;
+
+	hci_req(hci_sock, hcicmd, 0, NULL, 0, &rp, sizeof(rp));
+
+	*voice = rp;
+	printf("rp.settings = %x\n", rp);
+
+	result_err = 0;
+
+voice_err:
+
+	close(hci_sock);
+
+
+	return result_err;
+}
+
+static void 
+convert441002chto80001ch(uint8_t *input, int inputlen, uint8_t *output, int outputlen)
+{
+	int i, j, count;
+	uint8_t *tmpbuff = malloc(inputlen);
+	int16_t in_left, in_right, out_avg;
+
+	/* Mix 2 channels to 1 channel */
+	j = 0;
+	for (i = 0; j < (inputlen / 2); i= i + 4) {
+		in_left = (input[i+1] << 8) | input[i+0];
+		in_right = (input[i+3] << 8) | input[i+2];
+		out_avg = (in_left + in_right) / 2;
+
+		tmpbuff[j+0] = out_avg & 0xFF;
+		tmpbuff[j+1] = (out_avg & 0xFF00) >> 8;
+		j = j + 2;
+	}
+
+	/* down convert frequency taking 1 in 5 samples */
+	count = 44100 + 8000;
+	j = 0;
+	for (i = 0; j < outputlen ; i = i + 2) {
+
+		count += 8000;
+		if (count > 44100) {
+			count -= 44100;
+			output[j+0] = tmpbuff[i+0];
+			output[j+1] = tmpbuff[i+1];
+			j = j + 2;
+		}
+	}
+}
+
+static void 
+convert80001chto441002ch(uint8_t *input, int inputlen, uint8_t *output, int outputlen)
+{
+	int i, j, count;
+	uint8_t *tmpbuff = malloc(inputlen * 2);
+	int16_t in_left, in_right;
+
+	/* Mix 1 channel to 2 channels */
+	j = 0;
+	for (i = 0; j < inputlen * 2; i= i + 2) {
+		in_left = (input[i+1] << 8) | input[i+0];
+		in_right = in_left;
+
+		tmpbuff[j+0] = in_left & 0xFF;
+		tmpbuff[j+1] = (in_left & 0xFF00) >> 8;
+		tmpbuff[j+2] = in_right & 0xFF;
+		tmpbuff[j+3] = (in_right & 0xFF00) >> 8;
+		j = j + 4;
+	}
+
+	/* down convert frequency taking 1 in 5 samples */
+	count = 0;
+	j = i = 0;
+	while (j < outputlen) {
+		if (count >= 44100) {
+			count -= 44100;
+			i = i + 4;
+		}
+
+		output[j+0] = tmpbuff[i+0];
+		output[j+1] = tmpbuff[i+1];
+		output[j+2] = tmpbuff[i+2];
+		output[j+3] = tmpbuff[i+3];
+
+		count += 8000;
+		j = j + 4;
+		
+	}
+}
+
+static void 
+convertmulawtolinear16(uint8_t *input, int inputlen, uint8_t *output, int outputlen)
+{
+/*
+ * This table converts a (8 bit) mu-law value to a 16 bit value.
+ * The 16 bits are represented as an array of two bytes for easier access
+ * to the individual bytes.
+ */
+static const uint8_t mulawtolin16[256][2] = {
+	{0x02,0x84}, {0x06,0x84}, {0x0a,0x84}, {0x0e,0x84},
+	{0x12,0x84}, {0x16,0x84}, {0x1a,0x84}, {0x1e,0x84},
+	{0x22,0x84}, {0x26,0x84}, {0x2a,0x84}, {0x2e,0x84},
+	{0x32,0x84}, {0x36,0x84}, {0x3a,0x84}, {0x3e,0x84},
+	{0x41,0x84}, {0x43,0x84}, {0x45,0x84}, {0x47,0x84},
+	{0x49,0x84}, {0x4b,0x84}, {0x4d,0x84}, {0x4f,0x84},
+	{0x51,0x84}, {0x53,0x84}, {0x55,0x84}, {0x57,0x84},
+	{0x59,0x84}, {0x5b,0x84}, {0x5d,0x84}, {0x5f,0x84},
+	{0x61,0x04}, {0x62,0x04}, {0x63,0x04}, {0x64,0x04},
+	{0x65,0x04}, {0x66,0x04}, {0x67,0x04}, {0x68,0x04},
+	{0x69,0x04}, {0x6a,0x04}, {0x6b,0x04}, {0x6c,0x04},
+	{0x6d,0x04}, {0x6e,0x04}, {0x6f,0x04}, {0x70,0x04},
+	{0x70,0xc4}, {0x71,0x44}, {0x71,0xc4}, {0x72,0x44},
+	{0x72,0xc4}, {0x73,0x44}, {0x73,0xc4}, {0x74,0x44},
+	{0x74,0xc4}, {0x75,0x44}, {0x75,0xc4}, {0x76,0x44},
+	{0x76,0xc4}, {0x77,0x44}, {0x77,0xc4}, {0x78,0x44},
+	{0x78,0xa4}, {0x78,0xe4}, {0x79,0x24}, {0x79,0x64},
+	{0x79,0xa4}, {0x79,0xe4}, {0x7a,0x24}, {0x7a,0x64},
+	{0x7a,0xa4}, {0x7a,0xe4}, {0x7b,0x24}, {0x7b,0x64},
+	{0x7b,0xa4}, {0x7b,0xe4}, {0x7c,0x24}, {0x7c,0x64},
+	{0x7c,0x94}, {0x7c,0xb4}, {0x7c,0xd4}, {0x7c,0xf4},
+	{0x7d,0x14}, {0x7d,0x34}, {0x7d,0x54}, {0x7d,0x74},
+	{0x7d,0x94}, {0x7d,0xb4}, {0x7d,0xd4}, {0x7d,0xf4},
+	{0x7e,0x14}, {0x7e,0x34}, {0x7e,0x54}, {0x7e,0x74},
+	{0x7e,0x8c}, {0x7e,0x9c}, {0x7e,0xac}, {0x7e,0xbc},
+	{0x7e,0xcc}, {0x7e,0xdc}, {0x7e,0xec}, {0x7e,0xfc},
+	{0x7f,0x0c}, {0x7f,0x1c}, {0x7f,0x2c}, {0x7f,0x3c},
+	{0x7f,0x4c}, {0x7f,0x5c}, {0x7f,0x6c}, {0x7f,0x7c},
+	{0x7f,0x88}, {0x7f,0x90}, {0x7f,0x98}, {0x7f,0xa0},
+	{0x7f,0xa8}, {0x7f,0xb0}, {0x7f,0xb8}, {0x7f,0xc0},
+	{0x7f,0xc8}, {0x7f,0xd0}, {0x7f,0xd8}, {0x7f,0xe0},
+	{0x7f,0xe8}, {0x7f,0xf0}, {0x7f,0xf8}, {0x80,0x00},
+	{0xfd,0x7c}, {0xf9,0x7c}, {0xf5,0x7c}, {0xf1,0x7c},
+	{0xed,0x7c}, {0xe9,0x7c}, {0xe5,0x7c}, {0xe1,0x7c},
+	{0xdd,0x7c}, {0xd9,0x7c}, {0xd5,0x7c}, {0xd1,0x7c},
+	{0xcd,0x7c}, {0xc9,0x7c}, {0xc5,0x7c}, {0xc1,0x7c},
+	{0xbe,0x7c}, {0xbc,0x7c}, {0xba,0x7c}, {0xb8,0x7c},
+	{0xb6,0x7c}, {0xb4,0x7c}, {0xb2,0x7c}, {0xb0,0x7c},
+	{0xae,0x7c}, {0xac,0x7c}, {0xaa,0x7c}, {0xa8,0x7c},
+	{0xa6,0x7c}, {0xa4,0x7c}, {0xa2,0x7c}, {0xa0,0x7c},
+	{0x9e,0xfc}, {0x9d,0xfc}, {0x9c,0xfc}, {0x9b,0xfc},
+	{0x9a,0xfc}, {0x99,0xfc}, {0x98,0xfc}, {0x97,0xfc},
+	{0x96,0xfc}, {0x95,0xfc}, {0x94,0xfc}, {0x93,0xfc},
+	{0x92,0xfc}, {0x91,0xfc}, {0x90,0xfc}, {0x8f,0xfc},
+	{0x8f,0x3c}, {0x8e,0xbc}, {0x8e,0x3c}, {0x8d,0xbc},
+	{0x8d,0x3c}, {0x8c,0xbc}, {0x8c,0x3c}, {0x8b,0xbc},
+	{0x8b,0x3c}, {0x8a,0xbc}, {0x8a,0x3c}, {0x89,0xbc},
+	{0x89,0x3c}, {0x88,0xbc}, {0x88,0x3c}, {0x87,0xbc},
+	{0x87,0x5c}, {0x87,0x1c}, {0x86,0xdc}, {0x86,0x9c},
+	{0x86,0x5c}, {0x86,0x1c}, {0x85,0xdc}, {0x85,0x9c},
+	{0x85,0x5c}, {0x85,0x1c}, {0x84,0xdc}, {0x84,0x9c},
+	{0x84,0x5c}, {0x84,0x1c}, {0x83,0xdc}, {0x83,0x9c},
+	{0x83,0x6c}, {0x83,0x4c}, {0x83,0x2c}, {0x83,0x0c},
+	{0x82,0xec}, {0x82,0xcc}, {0x82,0xac}, {0x82,0x8c},
+	{0x82,0x6c}, {0x82,0x4c}, {0x82,0x2c}, {0x82,0x0c},
+	{0x81,0xec}, {0x81,0xcc}, {0x81,0xac}, {0x81,0x8c},
+	{0x81,0x74}, {0x81,0x64}, {0x81,0x54}, {0x81,0x44},
+	{0x81,0x34}, {0x81,0x24}, {0x81,0x14}, {0x81,0x04},
+	{0x80,0xf4}, {0x80,0xe4}, {0x80,0xd4}, {0x80,0xc4},
+	{0x80,0xb4}, {0x80,0xa4}, {0x80,0x94}, {0x80,0x84},
+	{0x80,0x78}, {0x80,0x70}, {0x80,0x68}, {0x80,0x60},
+	{0x80,0x58}, {0x80,0x50}, {0x80,0x48}, {0x80,0x40},
+	{0x80,0x38}, {0x80,0x30}, {0x80,0x28}, {0x80,0x20},
+	{0x80,0x18}, {0x80,0x10}, {0x80,0x08}, {0x80,0x00},
+};
+	int i, j;
+
+	j = 0;
+	for (i = 0; i < inputlen; i++) {
+		output[j + 0] = mulawtolin16[input[i + 0]][1];
+		output[j + 1] = mulawtolin16[input[i + 0]][0] ^ 0x80;
+		j += 2;
+	}
+}
+
+static void 
+convertlinear16tomulaw(uint8_t *input, int inputlen, uint8_t *output, int outputlen)
+{
+
+static const uint8_t lintomulaw[256] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+	0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
+	0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05,
+	0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07,
+	0x07, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09,
+	0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b,
+	0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d,
+	0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f,
+	0x0f, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x13,
+	0x13, 0x14, 0x14, 0x15, 0x15, 0x16, 0x16, 0x17,
+	0x17, 0x18, 0x18, 0x19, 0x19, 0x1a, 0x1a, 0x1b,
+	0x1b, 0x1c, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, 0x1f,
+	0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
+	0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
+	0x2f, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c,
+	0x3e, 0x41, 0x45, 0x49, 0x4d, 0x53, 0x5b, 0x67,
+	0xff, 0xe7, 0xdb, 0xd3, 0xcd, 0xc9, 0xc5, 0xc1,
+	0xbe, 0xbc, 0xba, 0xb8, 0xb6, 0xb4, 0xb2, 0xb0,
+	0xaf, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa8,
+	0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0,
+	0x9f, 0x9f, 0x9e, 0x9e, 0x9d, 0x9d, 0x9c, 0x9c,
+	0x9b, 0x9b, 0x9a, 0x9a, 0x99, 0x99, 0x98, 0x98,
+	0x97, 0x97, 0x96, 0x96, 0x95, 0x95, 0x94, 0x94,
+	0x93, 0x93, 0x92, 0x92, 0x91, 0x91, 0x90, 0x90,
+	0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e,
+	0x8d, 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c,
+	0x8b, 0x8b, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a,
+	0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88,
+	0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86,
+	0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84,
+	0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82,
+	0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80,
+};
+	int i, j;
+
+	j = 0;
+	for (i = 0; i < inputlen; i += 2) {
+		output[j + 0] = lintomulaw[input[i + 1] ^ 0x80];
+		j++;
+	}
+}
+
+static void 
+convertalawtolinear16(uint8_t *input, int inputlen, uint8_t *output, int outputlen)
+{
+
+static const uint8_t alawtolin16[256][2] = {
+	{0x6a,0x80}, {0x6b,0x80}, {0x68,0x80}, {0x69,0x80},
+	{0x6e,0x80}, {0x6f,0x80}, {0x6c,0x80}, {0x6d,0x80},
+	{0x62,0x80}, {0x63,0x80}, {0x60,0x80}, {0x61,0x80},
+	{0x66,0x80}, {0x67,0x80}, {0x64,0x80}, {0x65,0x80},
+	{0x75,0x40}, {0x75,0xc0}, {0x74,0x40}, {0x74,0xc0},
+	{0x77,0x40}, {0x77,0xc0}, {0x76,0x40}, {0x76,0xc0},
+	{0x71,0x40}, {0x71,0xc0}, {0x70,0x40}, {0x70,0xc0},
+	{0x73,0x40}, {0x73,0xc0}, {0x72,0x40}, {0x72,0xc0},
+	{0x2a,0x00}, {0x2e,0x00}, {0x22,0x00}, {0x26,0x00},
+	{0x3a,0x00}, {0x3e,0x00}, {0x32,0x00}, {0x36,0x00},
+	{0x0a,0x00}, {0x0e,0x00}, {0x02,0x00}, {0x06,0x00},
+	{0x1a,0x00}, {0x1e,0x00}, {0x12,0x00}, {0x16,0x00},
+	{0x55,0x00}, {0x57,0x00}, {0x51,0x00}, {0x53,0x00},
+	{0x5d,0x00}, {0x5f,0x00}, {0x59,0x00}, {0x5b,0x00},
+	{0x45,0x00}, {0x47,0x00}, {0x41,0x00}, {0x43,0x00},
+	{0x4d,0x00}, {0x4f,0x00}, {0x49,0x00}, {0x4b,0x00},
+	{0x7e,0xa8}, {0x7e,0xb8}, {0x7e,0x88}, {0x7e,0x98},
+	{0x7e,0xe8}, {0x7e,0xf8}, {0x7e,0xc8}, {0x7e,0xd8},
+	{0x7e,0x28}, {0x7e,0x38}, {0x7e,0x08}, {0x7e,0x18},
+	{0x7e,0x68}, {0x7e,0x78}, {0x7e,0x48}, {0x7e,0x58},
+	{0x7f,0xa8}, {0x7f,0xb8}, {0x7f,0x88}, {0x7f,0x98},
+	{0x7f,0xe8}, {0x7f,0xf8}, {0x7f,0xc8}, {0x7f,0xd8},
+	{0x7f,0x28}, {0x7f,0x38}, {0x7f,0x08}, {0x7f,0x18},
+	{0x7f,0x68}, {0x7f,0x78}, {0x7f,0x48}, {0x7f,0x58},
+	{0x7a,0xa0}, {0x7a,0xe0}, {0x7a,0x20}, {0x7a,0x60},
+	{0x7b,0xa0}, {0x7b,0xe0}, {0x7b,0x20}, {0x7b,0x60},
+	{0x78,0xa0}, {0x78,0xe0}, {0x78,0x20}, {0x78,0x60},
+	{0x79,0xa0}, {0x79,0xe0}, {0x79,0x20}, {0x79,0x60},
+	{0x7d,0x50}, {0x7d,0x70}, {0x7d,0x10}, {0x7d,0x30},
+	{0x7d,0xd0}, {0x7d,0xf0}, {0x7d,0x90}, {0x7d,0xb0},
+	{0x7c,0x50}, {0x7c,0x70}, {0x7c,0x10}, {0x7c,0x30},
+	{0x7c,0xd0}, {0x7c,0xf0}, {0x7c,0x90}, {0x7c,0xb0},
+	{0x95,0x80}, {0x94,0x80}, {0x97,0x80}, {0x96,0x80},
+	{0x91,0x80}, {0x90,0x80}, {0x93,0x80}, {0x92,0x80},
+	{0x9d,0x80}, {0x9c,0x80}, {0x9f,0x80}, {0x9e,0x80},
+	{0x99,0x80}, {0x98,0x80}, {0x9b,0x80}, {0x9a,0x80},
+	{0x8a,0xc0}, {0x8a,0x40}, {0x8b,0xc0}, {0x8b,0x40},
+	{0x88,0xc0}, {0x88,0x40}, {0x89,0xc0}, {0x89,0x40},
+	{0x8e,0xc0}, {0x8e,0x40}, {0x8f,0xc0}, {0x8f,0x40},
+	{0x8c,0xc0}, {0x8c,0x40}, {0x8d,0xc0}, {0x8d,0x40},
+	{0xd6,0x00}, {0xd2,0x00}, {0xde,0x00}, {0xda,0x00},
+	{0xc6,0x00}, {0xc2,0x00}, {0xce,0x00}, {0xca,0x00},
+	{0xf6,0x00}, {0xf2,0x00}, {0xfe,0x00}, {0xfa,0x00},
+	{0xe6,0x00}, {0xe2,0x00}, {0xee,0x00}, {0xea,0x00},
+	{0xab,0x00}, {0xa9,0x00}, {0xaf,0x00}, {0xad,0x00},
+	{0xa3,0x00}, {0xa1,0x00}, {0xa7,0x00}, {0xa5,0x00},
+	{0xbb,0x00}, {0xb9,0x00}, {0xbf,0x00}, {0xbd,0x00},
+	{0xb3,0x00}, {0xb1,0x00}, {0xb7,0x00}, {0xb5,0x00},
+	{0x81,0x58}, {0x81,0x48}, {0x81,0x78}, {0x81,0x68},
+	{0x81,0x18}, {0x81,0x08}, {0x81,0x38}, {0x81,0x28},
+	{0x81,0xd8}, {0x81,0xc8}, {0x81,0xf8}, {0x81,0xe8},
+	{0x81,0x98}, {0x81,0x88}, {0x81,0xb8}, {0x81,0xa8},
+	{0x80,0x58}, {0x80,0x48}, {0x80,0x78}, {0x80,0x68},
+	{0x80,0x18}, {0x80,0x08}, {0x80,0x38}, {0x80,0x28},
+	{0x80,0xd8}, {0x80,0xc8}, {0x80,0xf8}, {0x80,0xe8},
+	{0x80,0x98}, {0x80,0x88}, {0x80,0xb8}, {0x80,0xa8},
+	{0x85,0x60}, {0x85,0x20}, {0x85,0xe0}, {0x85,0xa0},
+	{0x84,0x60}, {0x84,0x20}, {0x84,0xe0}, {0x84,0xa0},
+	{0x87,0x60}, {0x87,0x20}, {0x87,0xe0}, {0x87,0xa0},
+	{0x86,0x60}, {0x86,0x20}, {0x86,0xe0}, {0x86,0xa0},
+	{0x82,0xb0}, {0x82,0x90}, {0x82,0xf0}, {0x82,0xd0},
+	{0x82,0x30}, {0x82,0x10}, {0x82,0x70}, {0x82,0x50},
+	{0x83,0xb0}, {0x83,0x90}, {0x83,0xf0}, {0x83,0xd0},
+	{0x83,0x30}, {0x83,0x10}, {0x83,0x70}, {0x83,0x50},
+};
+	int i, j;
+
+	j = 0;
+	for (i = 0; i < inputlen; i++) {
+		output[j + 0] = alawtolin16[input[i + 0]][1];
+		output[j + 1] = alawtolin16[input[i + 0]][0] ^ 0x80;
+		j += 2;
+	}
+}
+
+static void 
+convertlinear16toalaw(uint8_t *input, int inputlen, uint8_t *output, int outputlen)
+{
+
+static const uint8_t lintoalaw[256] = {
+	0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b,
+	0x28, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x29,
+	0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f,
+	0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d,
+	0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23,
+	0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21,
+	0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27,
+	0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25,
+	0x3a, 0x3a, 0x3b, 0x3b, 0x38, 0x38, 0x39, 0x39,
+	0x3e, 0x3e, 0x3f, 0x3f, 0x3c, 0x3c, 0x3d, 0x3d,
+	0x32, 0x32, 0x33, 0x33, 0x30, 0x30, 0x31, 0x31,
+	0x36, 0x36, 0x37, 0x37, 0x34, 0x34, 0x35, 0x35,
+	0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d,
+	0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05,
+	0x1a, 0x18, 0x1e, 0x1c, 0x12, 0x10, 0x16, 0x14,
+	0x6a, 0x6e, 0x62, 0x66, 0x7a, 0x72, 0x4a, 0x5a,
+	0xd5, 0xc5, 0xf5, 0xfd, 0xe5, 0xe1, 0xed, 0xe9,
+	0x95, 0x97, 0x91, 0x93, 0x9d, 0x9f, 0x99, 0x9b,
+	0x85, 0x84, 0x87, 0x86, 0x81, 0x80, 0x83, 0x82,
+	0x8d, 0x8c, 0x8f, 0x8e, 0x89, 0x88, 0x8b, 0x8a,
+	0xb5, 0xb5, 0xb4, 0xb4, 0xb7, 0xb7, 0xb6, 0xb6,
+	0xb1, 0xb1, 0xb0, 0xb0, 0xb3, 0xb3, 0xb2, 0xb2,
+	0xbd, 0xbd, 0xbc, 0xbc, 0xbf, 0xbf, 0xbe, 0xbe,
+	0xb9, 0xb9, 0xb8, 0xb8, 0xbb, 0xbb, 0xba, 0xba,
+	0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4,
+	0xa7, 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6,
+	0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0,
+	0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2,
+	0xad, 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac,
+	0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae,
+	0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8,
+	0xab, 0xab, 0xab, 0xab, 0xaa, 0xaa, 0xaa, 0xaa,
+};
+	int i, j;
+
+	j = 0;
+	for (i = 0; i < inputlen; i += 2) {
+		output[j + 0] = lintoalaw[input[i + 1] ^ 0x80];
+		j++;
+	}
+}
+
+static void 
+convertlinear16tolinear8(uint8_t *input, int inputlen, uint8_t *output, int outputlen)
+{
+
+	int i, j;
+
+	j = 0;
+	for (i = 0; i < inputlen; i += 2) {
+		output[j + 0] = input[i + 1];
+		j++;
+	}
+}
+
+static void 
+convertlinear8tolinear16(uint8_t *input, int inputlen, uint8_t *output, int outputlen)
+{
+
+	int i, j;
+
+	j = 0;
+	for (i = 0; i < inputlen; i++) {
+		output[j + 0] = 0;
+		output[j + 1] = input[i + 0];
+		j += 2;
+	}
+}
+


I hope this helps.

Regards,
Nat.

>Release-Note:

>Audit-Trail:

Responsible-Changed-From-To: bin-bug-people->plunky
Responsible-Changed-By: plunky@NetBSD.org
Responsible-Changed-When: Sun, 28 Jul 2013 18:57:51 +0000
Responsible-Changed-Why:
I will look at this (I am away sailing for a couple of weeks so can't do it right away)
I think it is a good idea to use the pad(4) device, which did not exist at the time
that bthset was written..

is it possible to modify the pad(4) driver to handle the type translations internally?
since the kernel does have some encoding conversion capability already..

iain


From: Nat Sloss <nathanialsloss@yahoo.com.au>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: bin/48090 (bthset to use pad devices and support 8 bit sco audio)
Date: Mon, 26 Aug 2013 11:18:15 +1000

 Hi Iain,

 While its possible to add mulaw, alaw and 8-bit slinear formats to pad. (I've 
 been experimenting) I've found a problem with the way this would work with 
 bthset using pad as I've seen the audio format change depending on what 
 you're trying to play.  Also as far as I understand opening /dev/audio would 
 default to mulaw 8000hz mono, so bthset would have to set the audio 
 parameters after the program playing audio and I don't think this would be 
 easily implemented.  

 So far I don't think it is practical to do the audio conversion as apart of 
 pad.  The tables I've included in bthset are duplicates fron mulaw.c and the 
 audio conversions are modified versions of audio filters fron auconv.c.  I 
 think even though there is code duplication and bthset is bigger this is 
 simple solution to handling the different formats.

 An interesting note the only other bluetooth sco audio formats that are not 
 supported so far are ulinear 8 and 16 bit, as I have no way of testing these 
 (not supported by my bluetooth adaptor) but I can submit my attempt at adding 
 support if you think it is a good idea.

 Regards,

 Nat.

>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2007 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.