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