#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 *);
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));
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);
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);
}
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;
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);
}
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);
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);
return (retval);
}
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));
}
int
avdtpSendAccept(int fd, uint8_t trans, uint8_t myCommand)
{
return (avdtpSendPacket(fd, myCommand, trans, RESPONSEACCEPT,
NULL, 0, NULL, 0));
}
int
avdtpSendReject(int fd, uint8_t trans, uint8_t myCommand)
{
uint8_t value = 0;
return (avdtpSendPacket(fd, myCommand, trans, RESPONSEREJECT,
&value, 1, NULL, 0));
}
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));
}
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;
}
retval = avdtpAutoConfig(cfg);
if (retval == 0)
return (0);
}
return (retval);
}
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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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;
switch (info.buffer_data[i + 3]) {
case 0:
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:
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;
}
}
i += 2 + info.buffer_data[i + 1];
}
aacMode1 &= cfg->aacMode1;
aacMode2 &= cfg->aacMode2;
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
}
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;
}
}
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;
switch (info.buffer_data[i + 3]) {
case 0:
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:
default:
break;
}
}
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);
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:
default:
err:
avdtpSendReject(cfg->hc, info.trans, info.signalID);
return (-ENXIO);
}
return (retval);
}