aboutsummaryrefslogtreecommitdiff
path: root/lib/virtual_oss/bt/avdtp.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/virtual_oss/bt/avdtp.c')
-rw-r--r--lib/virtual_oss/bt/avdtp.c720
1 files changed, 720 insertions, 0 deletions
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);
+}