git: 9cab9fde5eda - main - virtual_oss: Port to base
- Reply: Peter Jeremy : "Re: git: 9cab9fde5eda - main - virtual_oss: Port to base"
- Reply: Cy Schubert : "Re: git: 9cab9fde5eda - main - virtual_oss: Port to base"
- Reply: Dag-Erling_Smørgrav : "Re: git: 9cab9fde5eda - main - virtual_oss: Port to base"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 28 Sep 2025 09:58:53 UTC
The branch main has been updated by christos:
URL: https://cgit.FreeBSD.org/src/commit/?id=9cab9fde5edad9b409dd2317a2aec7815e6d6bed
commit 9cab9fde5edad9b409dd2317a2aec7815e6d6bed
Author: Christos Margiolis <christos@FreeBSD.org>
AuthorDate: 2025-09-28 09:56:52 +0000
Commit: Christos Margiolis <christos@FreeBSD.org>
CommitDate: 2025-09-28 09:56:52 +0000
virtual_oss: Port to base
This patch diverges quite a bit from the current upstream [1] in a few
ways:
1. virtual_oss(8), virtual_bt_speaker(8) and virtual_oss_cmd(8) are
actually separate programs.
2. Backends (lib/virtual_oss) are built as separate shared libraries and
we dlopen() them in virtual_oss(8) and virtual_bt_speaker(8) on
demand.
3. virtual_equalizer(8) and the sndio and bluetooth backends are built
as ports, because they depend on third-party libraries.
4. Use newer libav API in bluetooth backend (see HAVE_LIBAV ifdefs) to
address compiler errors.
[1] https://github.com/freebsd/virtual_oss
Sponsored by: The FreeBSD Foundation
MFC after: 1 week
Reviewed by: emaste
Differential Revision: https://reviews.freebsd.org/D52308
---
etc/mtree/BSD.lib32.dist | 2 +
etc/mtree/BSD.usr.dist | 2 +
lib/Makefile | 4 +-
lib/virtual_oss/Makefile | 9 +
lib/virtual_oss/Makefile.inc | 3 +
lib/virtual_oss/bt/Makefile | 19 +
lib/virtual_oss/bt/avdtp.c | 720 ++++++
lib/virtual_oss/bt/avdtp_signal.h | 139 ++
lib/virtual_oss/bt/bt.c | 1061 ++++++++
lib/virtual_oss/bt/bt.h | 116 +
lib/virtual_oss/bt/cosdata-gen/Makefile | 12 +
lib/virtual_oss/bt/cosdata-gen/cosdata.c | 177 ++
lib/virtual_oss/bt/sbc_coeffs.h | 69 +
lib/virtual_oss/bt/sbc_encode.c | 701 ++++++
lib/virtual_oss/bt/sbc_encode.h | 82 +
lib/virtual_oss/null/Makefile | 10 +
lib/virtual_oss/null/null.c | 102 +
lib/virtual_oss/oss/Makefile | 10 +
lib/virtual_oss/oss/oss.c | 197 ++
lib/virtual_oss/sndio/Makefile | 12 +
lib/virtual_oss/sndio/sndio.c | 203 ++
libexec/rc/rc.d/Makefile | 1 +
libexec/rc/rc.d/virtual_oss | 119 +
usr.sbin/Makefile | 1 +
usr.sbin/virtual_oss/Makefile | 8 +
usr.sbin/virtual_oss/Makefile.inc | 1 +
usr.sbin/virtual_oss/virtual_bt_speaker/Makefile | 11 +
.../virtual_oss/virtual_bt_speaker/bt_speaker.c | 542 ++++
.../virtual_bt_speaker/virtual_bt_speaker.8 | 71 +
usr.sbin/virtual_oss/virtual_equalizer/Makefile | 11 +
usr.sbin/virtual_oss/virtual_equalizer/equalizer.c | 431 ++++
.../virtual_equalizer/virtual_equalizer.8 | 127 +
usr.sbin/virtual_oss/virtual_oss/Makefile | 24 +
usr.sbin/virtual_oss/virtual_oss/audio_delay.c | 238 ++
usr.sbin/virtual_oss/virtual_oss/backend.h | 53 +
usr.sbin/virtual_oss/virtual_oss/compressor.c | 76 +
usr.sbin/virtual_oss/virtual_oss/ctl.c | 615 +++++
usr.sbin/virtual_oss/virtual_oss/eq.c | 226 ++
usr.sbin/virtual_oss/virtual_oss/format.c | 429 ++++
usr.sbin/virtual_oss/virtual_oss/httpd.c | 844 +++++++
usr.sbin/virtual_oss/virtual_oss/int.h | 327 +++
usr.sbin/virtual_oss/virtual_oss/main.c | 2625 ++++++++++++++++++++
usr.sbin/virtual_oss/virtual_oss/mul.c | 175 ++
usr.sbin/virtual_oss/virtual_oss/ring.c | 213 ++
usr.sbin/virtual_oss/virtual_oss/utils.h | 31 +
usr.sbin/virtual_oss/virtual_oss/virtual_oss.8 | 355 +++
usr.sbin/virtual_oss/virtual_oss/virtual_oss.c | 914 +++++++
usr.sbin/virtual_oss/virtual_oss/virtual_oss.h | 206 ++
usr.sbin/virtual_oss/virtual_oss_cmd/Makefile | 8 +
usr.sbin/virtual_oss/virtual_oss_cmd/command.c | 113 +
.../virtual_oss/virtual_oss_cmd/virtual_oss_cmd.8 | 103 +
51 files changed, 12547 insertions(+), 1 deletion(-)
diff --git a/etc/mtree/BSD.lib32.dist b/etc/mtree/BSD.lib32.dist
index a736a7d58b66..6520b7b95116 100644
--- a/etc/mtree/BSD.lib32.dist
+++ b/etc/mtree/BSD.lib32.dist
@@ -21,5 +21,7 @@
..
pkgconfig
..
+ virtual_oss
+ ..
..
..
diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist
index 19da845e962f..1945c26ebc5f 100644
--- a/etc/mtree/BSD.usr.dist
+++ b/etc/mtree/BSD.usr.dist
@@ -103,6 +103,8 @@
..
ossl-modules
..
+ virtual_oss
+ ..
..
libdata
ldscripts
diff --git a/lib/Makefile b/lib/Makefile
index 2b7cf2fdcb7d..bf38a489911d 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -115,7 +115,8 @@ SUBDIR= ${SUBDIR_BOOTSTRAP} \
libz \
libzstd \
ncurses \
- nss_tacplus
+ nss_tacplus \
+ virtual_oss
# Inter-library dependencies. When the makefile for a library contains LDADD
# libraries, those libraries should be listed as build order dependencies here.
@@ -157,6 +158,7 @@ SUBDIR_DEPEND_liblzma= libthr
SUBDIR_DEPEND_libpcap= ofed
.endif
SUBDIR_DEPEND_nss_tacplus= libtacplus
+SUBDIR_DEPEND_virtual_oss= libsamplerate
# NB: keep these sorted by MK_* knobs
diff --git a/lib/virtual_oss/Makefile b/lib/virtual_oss/Makefile
new file mode 100644
index 000000000000..dc83edd4b980
--- /dev/null
+++ b/lib/virtual_oss/Makefile
@@ -0,0 +1,9 @@
+.include <src.opts.mk>
+
+SHLIBDIR?= ${LIBDIR}/virtual_oss
+
+SUBDIR+= null \
+ oss
+
+.include "Makefile.inc"
+.include <bsd.subdir.mk>
diff --git a/lib/virtual_oss/Makefile.inc b/lib/virtual_oss/Makefile.inc
new file mode 100644
index 000000000000..45c8e0b1fdfc
--- /dev/null
+++ b/lib/virtual_oss/Makefile.inc
@@ -0,0 +1,3 @@
+.include "../Makefile.inc"
+
+LDFLAGS+= -L${.OBJDIR:H:H}/libsamplerate
diff --git a/lib/virtual_oss/bt/Makefile b/lib/virtual_oss/bt/Makefile
new file mode 100644
index 000000000000..15413b7a1f1e
--- /dev/null
+++ b/lib/virtual_oss/bt/Makefile
@@ -0,0 +1,19 @@
+SHLIB_NAME= voss_bt.so
+SHLIBDIR= ${LIBDIR}/virtual_oss
+
+SRCS= bt.c \
+ avdtp.c \
+ sbc_encode.c
+
+CFLAGS+= -I${SRCTOP}/usr.sbin/virtual_oss/virtual_oss \
+ -I${SRCTOP}/contrib/libsamplerate
+LDFLAGS+= -lbluetooth -lsdp
+LIBADD= samplerate
+
+.if defined(HAVE_LIBAV)
+CFLAGS+= -I${LOCALBASE:U/usr/local}/include -DHAVE_LIBAV
+LDFLAGS+= -L${LOCALBASE:U/usr/local}/lib \
+ -lavdevice -lavutil -lavcodec -lavformat
+.endif
+
+.include <bsd.lib.mk>
diff --git a/lib/virtual_oss/bt/avdtp.c b/lib/virtual_oss/bt/avdtp.c
new file mode 100644
index 000000000000..82ed0fb942b6
--- /dev/null
+++ b/lib/virtual_oss/bt/avdtp.c
@@ -0,0 +1,720 @@
+/* $NetBSD$ */
+
+/*-
+ * Copyright (c) 2015-2016 Nathanial Sloss <nathanialsloss@yahoo.com.au>
+ * Copyright (c) 2016-2019 Hans Petter Selasky <hps@selasky.org>
+ * Copyright (c) 2019 Google LLC, written by Richard Kralovic <riso@google.com>
+ *
+ * This software is dedicated to the memory of -
+ * Baron James Anlezark (Barry) - 1 Jan 1949 - 13 May 2012.
+ *
+ * Barry was a man who loved his music.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/uio.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "avdtp_signal.h"
+#include "bt.h"
+
+#define DPRINTF(...) printf("backend_bt: " __VA_ARGS__)
+
+struct avdtpGetPacketInfo {
+ uint8_t buffer_data[512];
+ uint16_t buffer_len;
+ uint8_t trans;
+ uint8_t signalID;
+};
+
+static int avdtpAutoConfig(struct bt_config *);
+
+/* Return received message type if success, < 0 if failure. */
+static int
+avdtpGetPacket(int fd, struct avdtpGetPacketInfo *info)
+{
+ uint8_t *pos = info->buffer_data;
+ uint8_t *end = info->buffer_data + sizeof(info->buffer_data);
+ uint8_t message_type;
+ int len;
+
+ memset(info, 0, sizeof(*info));
+
+ /* Handle fragmented packets */
+ for (int remaining = 1; remaining > 0; --remaining) {
+ len = read(fd, pos, end - pos);
+
+ if (len < AVDTP_LEN_SUCCESS)
+ return (-1);
+ if (len == (int)(end - pos))
+ return (-1); /* buffer too small */
+
+ uint8_t trans = (pos[0] & TRANSACTIONLABEL) >> TRANSACTIONLABEL_S;
+ uint8_t packet_type = (pos[0] & PACKETTYPE) >> PACKETTYPE_S;
+ uint8_t current_message_type = (info->buffer_data[0] & MESSAGETYPE);
+ uint8_t shift;
+ if (pos == info->buffer_data) {
+ info->trans = trans;
+ message_type = current_message_type;
+ if (packet_type == singlePacket) {
+ info->signalID = (pos[1] & SIGNALID_MASK);
+ shift = 2;
+ } else {
+ if (packet_type != startPacket)
+ return (-1);
+ remaining = pos[1];
+ info->signalID = (pos[2] & SIGNALID_MASK);
+ shift = 3;
+ }
+ } else {
+ if (info->trans != trans ||
+ message_type != current_message_type ||
+ (remaining == 1 && packet_type != endPacket) ||
+ (remaining > 1 && packet_type != continuePacket)) {
+ return (-1);
+ }
+ shift = 1;
+ }
+ memmove(pos, pos + shift, len);
+ pos += len;
+ }
+ info->buffer_len = pos - info->buffer_data;
+ return (message_type);
+}
+
+/* Returns 0 on success, < 0 on failure. */
+static int
+avdtpSendPacket(int fd, uint8_t command, uint8_t trans, uint8_t type,
+ uint8_t * data0, int datasize0, uint8_t * data1,
+ int datasize1)
+{
+ struct iovec iov[3];
+ uint8_t header[2];
+ int retval;
+
+ /* fill out command header */
+ header[0] = (trans << 4) | (type & 3);
+ if (command != 0)
+ header[1] = command & 0x3f;
+ else
+ header[1] = 3;
+
+ iov[0].iov_base = header;
+ iov[0].iov_len = 2;
+ iov[1].iov_base = data0;
+ iov[1].iov_len = datasize0;
+ iov[2].iov_base = data1;
+ iov[2].iov_len = datasize1;
+
+ retval = writev(fd, iov, 3);
+ if (retval != (2 + datasize0 + datasize1))
+ return (-EINVAL);
+ else
+ return (0);
+}
+
+/* Returns 0 on success, < 0 on failure. */
+static int
+avdtpSendSyncCommand(int fd, struct avdtpGetPacketInfo *info,
+ uint8_t command, uint8_t type, uint8_t * data0,
+ int datasize0, uint8_t * data1, int datasize1)
+{
+ static uint8_t transLabel;
+ uint8_t trans;
+ int retval;
+
+ alarm(8); /* set timeout */
+
+ trans = (transLabel++) & 0xF;
+
+ retval = avdtpSendPacket(fd, command, trans, type,
+ data0, datasize0, data1, datasize1);
+ if (retval)
+ goto done;
+retry:
+ switch (avdtpGetPacket(fd, info)) {
+ case RESPONSEACCEPT:
+ if (info->trans != trans)
+ goto retry;
+ retval = 0;
+ break;
+ case RESPONSEREJECT:
+ if (info->trans != trans)
+ goto retry;
+ retval = -EINVAL;
+ break;
+ case COMMAND:
+ retval = avdtpSendReject(fd, info->trans, info->signalID);
+ if (retval == 0)
+ goto retry;
+ break;
+ default:
+ retval = -ENXIO;
+ break;
+ }
+done:
+ alarm(0); /* clear timeout */
+
+ return (retval);
+}
+
+/*
+ * Variant for acceptor role: We support any frequency, blocks, bands, and
+ * allocation. Returns 0 on success, < 0 on failure.
+ */
+static int
+avdtpSendCapabilitiesResponseSBCForACP(int fd, int trans)
+{
+ uint8_t data[10];
+
+ data[0] = mediaTransport;
+ data[1] = 0;
+ data[2] = mediaCodec;
+ data[3] = 0x6;
+ data[4] = mediaTypeAudio;
+ data[5] = SBC_CODEC_ID;
+ data[6] =
+ (1 << (3 - MODE_STEREO)) |
+ (1 << (3 - MODE_JOINT)) |
+ (1 << (3 - MODE_DUAL)) |
+ (1 << (3 - MODE_MONO)) |
+ (1 << (7 - FREQ_44_1K)) |
+ (1 << (7 - FREQ_48K)) |
+ (1 << (7 - FREQ_32K)) |
+ (1 << (7 - FREQ_16K));
+ data[7] =
+ (1 << (7 - BLOCKS_4)) |
+ (1 << (7 - BLOCKS_8)) |
+ (1 << (7 - BLOCKS_12)) |
+ (1 << (7 - BLOCKS_16)) |
+ (1 << (3 - BANDS_4)) |
+ (1 << (3 - BANDS_8)) | (1 << ALLOC_LOUDNESS) | (1 << ALLOC_SNR);
+ data[8] = MIN_BITPOOL;
+ data[9] = DEFAULT_MAXBPOOL;
+
+ return (avdtpSendPacket(fd, AVDTP_GET_CAPABILITIES, trans,
+ RESPONSEACCEPT, data, sizeof(data), NULL, 0));
+}
+
+/* Returns 0 on success, < 0 on failure. */
+int
+avdtpSendAccept(int fd, uint8_t trans, uint8_t myCommand)
+{
+ return (avdtpSendPacket(fd, myCommand, trans, RESPONSEACCEPT,
+ NULL, 0, NULL, 0));
+}
+
+/* Returns 0 on success, < 0 on failure. */
+int
+avdtpSendReject(int fd, uint8_t trans, uint8_t myCommand)
+{
+ uint8_t value = 0;
+
+ return (avdtpSendPacket(fd, myCommand, trans, RESPONSEREJECT,
+ &value, 1, NULL, 0));
+}
+
+/* Returns 0 on success, < 0 on failure. */
+int
+avdtpSendDiscResponseAudio(int fd, uint8_t trans,
+ uint8_t mySep, uint8_t is_sink)
+{
+ uint8_t data[2];
+
+ data[0] = mySep << 2;
+ data[1] = mediaTypeAudio << 4 | (is_sink ? (1 << 3) : 0);
+
+ return (avdtpSendPacket(fd, AVDTP_DISCOVER, trans, RESPONSEACCEPT,
+ data, 2, NULL, 0));
+}
+
+/* Returns 0 on success, < 0 on failure. */
+int
+avdtpDiscoverAndConfig(struct bt_config *cfg, bool isSink)
+{
+ struct avdtpGetPacketInfo info;
+ uint16_t offset;
+ uint8_t chmode = cfg->chmode;
+ uint8_t aacMode1 = cfg->aacMode1;
+ uint8_t aacMode2 = cfg->aacMode2;
+ int retval;
+
+ retval = avdtpSendSyncCommand(cfg->hc, &info, AVDTP_DISCOVER, 0,
+ NULL, 0, NULL, 0);
+ if (retval)
+ return (retval);
+
+ retval = -EBUSY;
+ for (offset = 0; offset + 2 <= info.buffer_len; offset += 2) {
+ cfg->sep = info.buffer_data[offset] >> 2;
+ cfg->media_Type = info.buffer_data[offset + 1] >> 4;
+ cfg->chmode = chmode;
+ cfg->aacMode1 = aacMode1;
+ cfg->aacMode2 = aacMode2;
+ if (info.buffer_data[offset] & DISCOVER_SEP_IN_USE)
+ continue;
+ if (info.buffer_data[offset + 1] & DISCOVER_IS_SINK) {
+ if (!isSink)
+ continue;
+ } else {
+ if (isSink)
+ continue;
+ }
+ /* try to configure SBC */
+ retval = avdtpAutoConfig(cfg);
+ if (retval == 0)
+ return (0);
+ }
+ return (retval);
+}
+
+/* Returns 0 on success, < 0 on failure. */
+static int
+avdtpGetCapabilities(int fd, uint8_t sep, struct avdtpGetPacketInfo *info)
+{
+ uint8_t address = (sep << 2);
+
+ return (avdtpSendSyncCommand(fd, info,
+ AVDTP_GET_CAPABILITIES, 0, &address, 1,
+ NULL, 0));
+}
+
+/* Returns 0 on success, < 0 on failure. */
+int
+avdtpSetConfiguration(int fd, uint8_t sep, uint8_t * data, int datasize)
+{
+ struct avdtpGetPacketInfo info;
+ uint8_t configAddresses[2];
+
+ configAddresses[0] = sep << 2;
+ configAddresses[1] = INTSEP << 2;
+
+ return (avdtpSendSyncCommand(fd, &info, AVDTP_SET_CONFIGURATION, 0,
+ configAddresses, 2, data, datasize));
+}
+
+/* Returns 0 on success, < 0 on failure. */
+int
+avdtpOpen(int fd, uint8_t sep)
+{
+ struct avdtpGetPacketInfo info;
+ uint8_t address = sep << 2;
+
+ return (avdtpSendSyncCommand(fd, &info, AVDTP_OPEN, 0,
+ &address, 1, NULL, 0));
+}
+
+/* Returns 0 on success, < 0 on failure. */
+int
+avdtpStart(int fd, uint8_t sep)
+{
+ struct avdtpGetPacketInfo info;
+ uint8_t address = sep << 2;
+
+ return (avdtpSendSyncCommand(fd, &info, AVDTP_START, 0,
+ &address, 1, NULL, 0));
+}
+
+/* Returns 0 on success, < 0 on failure. */
+int
+avdtpClose(int fd, uint8_t sep)
+{
+ struct avdtpGetPacketInfo info;
+ uint8_t address = sep << 2;
+
+ return (avdtpSendSyncCommand(fd, &info, AVDTP_CLOSE, 0,
+ &address, 1, NULL, 0));
+}
+
+/* Returns 0 on success, < 0 on failure. */
+int
+avdtpSuspend(int fd, uint8_t sep)
+{
+ struct avdtpGetPacketInfo info;
+ uint8_t address = sep << 2;
+
+ return (avdtpSendSyncCommand(fd, &info, AVDTP_SUSPEND, 0,
+ &address, 1, NULL, 0));
+}
+
+/* Returns 0 on success, < 0 on failure. */
+int
+avdtpAbort(int fd, uint8_t sep)
+{
+ struct avdtpGetPacketInfo info;
+ uint8_t address = sep << 2;
+
+ return (avdtpSendSyncCommand(fd, &info, AVDTP_ABORT, 0,
+ &address, 1, NULL, 0));
+}
+
+static int
+avdtpAutoConfig(struct bt_config *cfg)
+{
+ struct avdtpGetPacketInfo info;
+ uint8_t freqmode;
+ uint8_t blk_len_sb_alloc;
+ uint8_t availFreqMode = 0;
+ uint8_t availConfig = 0;
+ uint8_t supBitpoolMin = 0;
+ uint8_t supBitpoolMax = 0;
+ uint8_t aacMode1 = 0;
+ uint8_t aacMode2 = 0;
+#ifdef HAVE_LIBAV
+ uint8_t aacBitrate3 = 0;
+ uint8_t aacBitrate4 = 0;
+ uint8_t aacBitrate5 = 0;
+#endif
+ int retval;
+ int i;
+
+ retval = avdtpGetCapabilities(cfg->hc, cfg->sep, &info);
+ if (retval) {
+ DPRINTF("Cannot get capabilities\n");
+ return (retval);
+ }
+retry:
+ for (i = 0; (i + 1) < info.buffer_len;) {
+#if 0
+ DPRINTF("0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ info.buffer_data[i + 0],
+ info.buffer_data[i + 1],
+ info.buffer_data[i + 2],
+ info.buffer_data[i + 3],
+ info.buffer_data[i + 4], info.buffer_data[i + 5]);
+#endif
+ if (i + 2 + info.buffer_data[i + 1] > info.buffer_len)
+ break;
+ switch (info.buffer_data[i]) {
+ case mediaTransport:
+ break;
+ case mediaCodec:
+ if (info.buffer_data[i + 1] < 2)
+ break;
+ /* check codec */
+ switch (info.buffer_data[i + 3]) {
+ case 0: /* SBC */
+ if (info.buffer_data[i + 1] < 6)
+ break;
+ availFreqMode = info.buffer_data[i + 4];
+ availConfig = info.buffer_data[i + 5];
+ supBitpoolMin = info.buffer_data[i + 6];
+ supBitpoolMax = info.buffer_data[i + 7];
+ break;
+ case 2: /* MPEG2/4 AAC */
+ if (info.buffer_data[i + 1] < 8)
+ break;
+ aacMode1 = info.buffer_data[i + 5];
+ aacMode2 = info.buffer_data[i + 6];
+#ifdef HAVE_LIBAV
+ aacBitrate3 = info.buffer_data[i + 7];
+ aacBitrate4 = info.buffer_data[i + 8];
+ aacBitrate5 = info.buffer_data[i + 9];
+#endif
+ break;
+ default:
+ break;
+ }
+ }
+ /* jump to next information element */
+ i += 2 + info.buffer_data[i + 1];
+ }
+ aacMode1 &= cfg->aacMode1;
+ aacMode2 &= cfg->aacMode2;
+
+ /* Try AAC first */
+ if (aacMode1 == cfg->aacMode1 && aacMode2 == cfg->aacMode2) {
+#ifdef HAVE_LIBAV
+ uint8_t config[12] = { mediaTransport, 0x0, mediaCodec,
+ 0x8, 0x0, 0x02, 0x80, aacMode1, aacMode2, aacBitrate3,
+ aacBitrate4, aacBitrate5
+ };
+
+ if (avdtpSetConfiguration
+ (cfg->hc, cfg->sep, config, sizeof(config)) == 0) {
+ cfg->codec = CODEC_AAC;
+ return (0);
+ }
+#endif
+ }
+ /* Try SBC second */
+ if (cfg->freq == FREQ_UNDEFINED)
+ goto auto_config_failed;
+
+ freqmode = (1 << (3 - cfg->freq + 4)) | (1 << (3 - cfg->chmode));
+
+ if ((availFreqMode & freqmode) != freqmode) {
+ DPRINTF("No frequency and mode match\n");
+ goto auto_config_failed;
+ }
+ for (i = 0; i != 4; i++) {
+ blk_len_sb_alloc = (1 << (i + 4)) |
+ (1 << (1 - cfg->bands + 2)) | (1 << cfg->allocm);
+
+ if ((availConfig & blk_len_sb_alloc) == blk_len_sb_alloc)
+ break;
+ }
+ if (i == 4) {
+ DPRINTF("No bands available\n");
+ goto auto_config_failed;
+ }
+ cfg->blocks = (3 - i);
+
+ if (cfg->allocm == ALLOC_SNR)
+ supBitpoolMax &= ~1;
+
+ if (cfg->chmode == MODE_DUAL || cfg->chmode == MODE_MONO)
+ supBitpoolMax /= 2;
+
+ if (cfg->bands == BANDS_4)
+ supBitpoolMax /= 2;
+
+ if (supBitpoolMax > cfg->bitpool)
+ supBitpoolMax = cfg->bitpool;
+ else
+ cfg->bitpool = supBitpoolMax;
+
+ do {
+ uint8_t config[10] = { mediaTransport, 0x0, mediaCodec, 0x6,
+ 0x0, 0x0, freqmode, blk_len_sb_alloc, supBitpoolMin,
+ supBitpoolMax
+ };
+
+ if (avdtpSetConfiguration
+ (cfg->hc, cfg->sep, config, sizeof(config)) == 0) {
+ cfg->codec = CODEC_SBC;
+ return (0);
+ }
+ } while (0);
+
+auto_config_failed:
+ if (cfg->chmode == MODE_STEREO) {
+ cfg->chmode = MODE_MONO;
+ cfg->aacMode2 ^= 0x0C;
+ goto retry;
+ }
+ return (-EINVAL);
+}
+
+void
+avdtpACPFree(struct bt_config *cfg)
+{
+ if (cfg->handle.sbc_enc) {
+ free(cfg->handle.sbc_enc);
+ cfg->handle.sbc_enc = NULL;
+ }
+}
+
+/* Returns 0 on success, < 0 on failure. */
+static int
+avdtpParseSBCConfig(uint8_t * data, struct bt_config *cfg)
+{
+ if (data[0] & (1 << (7 - FREQ_48K))) {
+ cfg->freq = FREQ_48K;
+ } else if (data[0] & (1 << (7 - FREQ_44_1K))) {
+ cfg->freq = FREQ_44_1K;
+ } else if (data[0] & (1 << (7 - FREQ_32K))) {
+ cfg->freq = FREQ_32K;
+ } else if (data[0] & (1 << (7 - FREQ_16K))) {
+ cfg->freq = FREQ_16K;
+ } else {
+ return -EINVAL;
+ }
+
+ if (data[0] & (1 << (3 - MODE_STEREO))) {
+ cfg->chmode = MODE_STEREO;
+ } else if (data[0] & (1 << (3 - MODE_JOINT))) {
+ cfg->chmode = MODE_JOINT;
+ } else if (data[0] & (1 << (3 - MODE_DUAL))) {
+ cfg->chmode = MODE_DUAL;
+ } else if (data[0] & (1 << (3 - MODE_MONO))) {
+ cfg->chmode = MODE_MONO;
+ } else {
+ return -EINVAL;
+ }
+
+ if (data[1] & (1 << (7 - BLOCKS_16))) {
+ cfg->blocks = BLOCKS_16;
+ } else if (data[1] & (1 << (7 - BLOCKS_12))) {
+ cfg->blocks = BLOCKS_12;
+ } else if (data[1] & (1 << (7 - BLOCKS_8))) {
+ cfg->blocks = BLOCKS_8;
+ } else if (data[1] & (1 << (7 - BLOCKS_4))) {
+ cfg->blocks = BLOCKS_4;
+ } else {
+ return -EINVAL;
+ }
+
+ if (data[1] & (1 << (3 - BANDS_8))) {
+ cfg->bands = BANDS_8;
+ } else if (data[1] & (1 << (3 - BANDS_4))) {
+ cfg->bands = BANDS_4;
+ } else {
+ return -EINVAL;
+ }
+
+ if (data[1] & (1 << ALLOC_LOUDNESS)) {
+ cfg->allocm = ALLOC_LOUDNESS;
+ } else if (data[1] & (1 << ALLOC_SNR)) {
+ cfg->allocm = ALLOC_SNR;
+ } else {
+ return -EINVAL;
+ }
+ cfg->bitpool = data[3];
+ return 0;
+}
+
+int
+avdtpACPHandlePacket(struct bt_config *cfg)
+{
+ struct avdtpGetPacketInfo info;
+ int retval;
+
+ if (avdtpGetPacket(cfg->hc, &info) != COMMAND)
+ return (-ENXIO);
+
+ switch (info.signalID) {
+ case AVDTP_DISCOVER:
+ retval =
+ avdtpSendDiscResponseAudio(cfg->hc, info.trans, ACPSEP, 1);
+ if (!retval)
+ retval = AVDTP_DISCOVER;
+ break;
+ case AVDTP_GET_CAPABILITIES:
+ retval =
+ avdtpSendCapabilitiesResponseSBCForACP(cfg->hc, info.trans);
+ if (!retval)
+ retval = AVDTP_GET_CAPABILITIES;
+ break;
+ case AVDTP_SET_CONFIGURATION:
+ if (cfg->acceptor_state != acpInitial)
+ goto err;
+ cfg->sep = info.buffer_data[1] >> 2;
+ int is_configured = 0;
+ for (int i = 2; (i + 1) < info.buffer_len;) {
+ if (i + 2 + info.buffer_data[i + 1] > info.buffer_len)
+ break;
+ switch (info.buffer_data[i]) {
+ case mediaTransport:
+ break;
+ case mediaCodec:
+ if (info.buffer_data[i + 1] < 2)
+ break;
+ /* check codec */
+ switch (info.buffer_data[i + 3]) {
+ case 0: /* SBC */
+ if (info.buffer_data[i + 1] < 6)
+ break;
+ retval =
+ avdtpParseSBCConfig(info.buffer_data + i + 4, cfg);
+ if (retval)
+ return retval;
+ is_configured = 1;
+ break;
+ case 2: /* MPEG2/4 AAC */
+ /* TODO: Add support */
+ default:
+ break;
+ }
+ }
+ /* jump to next information element */
+ i += 2 + info.buffer_data[i + 1];
+ }
+ if (!is_configured)
+ goto err;
+
+ retval =
+ avdtpSendAccept(cfg->hc, info.trans, AVDTP_SET_CONFIGURATION);
+ if (retval)
+ return (retval);
+
+ /* TODO: Handle other codecs */
+ if (cfg->handle.sbc_enc == NULL) {
+ cfg->handle.sbc_enc = malloc(sizeof(*cfg->handle.sbc_enc));
+ if (cfg->handle.sbc_enc == NULL)
+ return (-ENOMEM);
+ }
+ memset(cfg->handle.sbc_enc, 0, sizeof(*cfg->handle.sbc_enc));
+
+ retval = AVDTP_SET_CONFIGURATION;
+ cfg->acceptor_state = acpConfigurationSet;
+ break;
+ case AVDTP_OPEN:
+ if (cfg->acceptor_state != acpConfigurationSet)
+ goto err;
+ retval = avdtpSendAccept(cfg->hc, info.trans, info.signalID);
+ if (retval)
+ return (retval);
+ retval = info.signalID;
+ cfg->acceptor_state = acpStreamOpened;
+ break;
+ case AVDTP_START:
+ if (cfg->acceptor_state != acpStreamOpened &&
+ cfg->acceptor_state != acpStreamSuspended) {
+ goto err;
+ }
+ retval = avdtpSendAccept(cfg->hc, info.trans, info.signalID);
+ if (retval)
+ return retval;
+ retval = info.signalID;
+ cfg->acceptor_state = acpStreamStarted;
+ break;
+ case AVDTP_CLOSE:
+ if (cfg->acceptor_state != acpStreamOpened &&
+ cfg->acceptor_state != acpStreamStarted &&
+ cfg->acceptor_state != acpStreamSuspended) {
+ goto err;
+ }
+ retval = avdtpSendAccept(cfg->hc, info.trans, info.signalID);
+ if (retval)
+ return (retval);
+ retval = info.signalID;
+ cfg->acceptor_state = acpStreamClosed;
+ break;
+ case AVDTP_SUSPEND:
+ if (cfg->acceptor_state != acpStreamOpened &&
+ cfg->acceptor_state != acpStreamStarted) {
+ goto err;
+ }
+ retval = avdtpSendAccept(cfg->hc, info.trans, info.signalID);
+ if (retval)
+ return (retval);
+ retval = info.signalID;
+ cfg->acceptor_state = acpStreamSuspended;
+ break;
+ case AVDTP_GET_CONFIGURATION:
+ case AVDTP_RECONFIGURE:
+ case AVDTP_ABORT:
+ /* TODO: Implement this. */
+ default:
+err:
+ avdtpSendReject(cfg->hc, info.trans, info.signalID);
+ return (-ENXIO);
+ }
+ return (retval);
+}
diff --git a/lib/virtual_oss/bt/avdtp_signal.h b/lib/virtual_oss/bt/avdtp_signal.h
new file mode 100644
index 000000000000..a46cc6dd9dcf
--- /dev/null
+++ b/lib/virtual_oss/bt/avdtp_signal.h
@@ -0,0 +1,139 @@
+/* $NetBSD$ */
+
+/*-
+ * Copyright (c) 2015 Nathanial Sloss <nathanialsloss@yahoo.com.au>
+ *
+ * This software is dedicated to the memory of -
+ * Baron James Anlezark (Barry) - 1 Jan 1949 - 13 May 2012.
+ *
+ * Barry was a man who loved his music.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _AVDTP_SIGNAL_H_
+#define _AVDTP_SIGNAL_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/* Our endpoint. */
+#define INTSEP 8
+#define ACPSEP 8
+
+/* AVDTP signals. */
+
+#define AVDTP_DISCOVER 0x01
+#define AVDTP_GET_CAPABILITIES 0x02
+#define AVDTP_SET_CONFIGURATION 0x03
+#define AVDTP_GET_CONFIGURATION 0x04
+#define AVDTP_RECONFIGURE 0x05
+#define AVDTP_OPEN 0x06
+#define AVDTP_START 0x07
+#define AVDTP_CLOSE 0x08
+#define AVDTP_SUSPEND 0x09
+#define AVDTP_ABORT 0x0a
+#define AVDTP_SECUURITY_CONTROL 0x0b
+
+/* Signal Command & Response Header Masks. */
+
+#define TRANSACTIONLABEL 0xf0
+#define TRANSACTIONLABEL_S 4
+#define SIGNALID_MASK 0x3f
+#define PACKETTYPE 0x0c
+#define PACKETTYPE_S 0x02
+#define MESSAGETYPE 0x03
+#define SIGNALIDENTIFIER 0x3f
+#define DISCOVER_SEP_IN_USE 0x02
+#define DISCOVER_IS_SINK 0x08
+
+/* Packet Types */
+#define singlePacket 0x0
+#define startPacket 0x1
+#define continuePacket 0x2
+#define endPacket 0x3
+
*** 11983 LINES SKIPPED ***