NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

bin/48090: bthset to use pad devices and support 8 bit sco audio



>Number:         48090
>Category:       bin
>Synopsis:       bthset to use pad devices and support 8 bit sco audio
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sun Jul 28 11:00: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.



Home | Main Index | Thread Index | Old Index