Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 28 Sep 2025 09:58:53 GMT
From:      Christos Margiolis <christos@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 9cab9fde5eda - main - virtual_oss: Port to base
Message-ID:  <202509280958.58S9wrww083342@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202509280958.58S9wrww083342>