bluez/bluez-5.50-a2dp-backports.patch
Stefan Seyfried d12a36af48 Accepting request 712248 from home:fcrozat:branches:Base:System
- Add avinfo to bluez-test, useful for debugging.
- Only BuildRequires pkgconfig(ell) on Tumbleweed.
- Add bluez-5.50-a2dp-backports.patch: A2DP fixes for newer codecs
  (upstream backport).

OBS-URL: https://build.opensuse.org/request/show/712248
OBS-URL: https://build.opensuse.org/package/show/Base:System/bluez?expand=0&rev=264
2019-06-30 12:53:42 +00:00

4715 lines
135 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 1e9dafa0dd062f6773481074d4a426ae4c6e2aea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sun, 23 Dec 2018 11:40:12 +0100
Subject: [PATCH 01/31] avinfo: Fix buffer overflow when parsing
broken/malicious data
Check size of buffer prior casting it to struct.
---
tools/avinfo.c | 91 +++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 71 insertions(+), 20 deletions(-)
diff --git a/tools/avinfo.c b/tools/avinfo.c
index 31c4e106e..47fa1d2c5 100644
--- a/tools/avinfo.c
+++ b/tools/avinfo.c
@@ -167,10 +167,15 @@ struct avdtp_content_protection_capability {
uint8_t data[0];
} __attribute__ ((packed));
-static void print_aptx(a2dp_aptx_t *aptx)
+static void print_aptx(a2dp_aptx_t *aptx, uint8_t size)
{
printf("\t\tVendor Specific Value (aptX)");
+ if (size < sizeof(*aptx)) {
+ printf(" (broken)\n");
+ return;
+ }
+
printf("\n\t\t\tFrequencies: ");
if (aptx->frequency & APTX_SAMPLING_FREQ_16000)
printf("16kHz ");
@@ -190,20 +195,33 @@ static void print_aptx(a2dp_aptx_t *aptx)
printf("\n");
}
-static void print_ldac(a2dp_ldac_t *ldac)
+static void print_ldac(a2dp_ldac_t *ldac, uint8_t size)
{
printf("\t\tVendor Specific Value (LDAC)");
+ if (size < sizeof(*ldac)) {
+ printf(" (broken)\n");
+ return;
+ }
+
printf("\n\t\t\tUnknown: %02x %02x", ldac->unknown[0],
ldac->unknown[1]);
printf("\n");
}
-static void print_vendor(a2dp_vendor_codec_t *vendor)
+static void print_vendor(a2dp_vendor_codec_t *vendor, uint8_t size)
{
- uint32_t vendor_id = btohl(vendor->vendor_id);
- uint16_t codec_id = btohs(vendor->codec_id);
+ uint32_t vendor_id;
+ uint16_t codec_id;
+
+ if (size < sizeof(*vendor)) {
+ printf("\tMedia Codec: Vendor Specific A2DP Codec (broken)");
+ return;
+ }
+
+ vendor_id = btohl(vendor->vendor_id);
+ codec_id = btohs(vendor->codec_id);
printf("\tMedia Codec: Vendor Specific A2DP Codec");
@@ -212,15 +230,22 @@ static void print_vendor(a2dp_vendor_codec_t *vendor)
printf("\n\t\tVendor Specific Codec ID 0x%04x\n", codec_id);
if (vendor_id == APTX_VENDOR_ID && codec_id == APTX_CODEC_ID)
- print_aptx((void *) vendor);
+ print_aptx((void *) vendor, size);
else if (vendor_id == LDAC_VENDOR_ID && codec_id == LDAC_CODEC_ID)
- print_ldac((void *) vendor);
+ print_ldac((void *) vendor, size);
}
-static void print_mpeg24(a2dp_aac_t *aac)
+static void print_mpeg24(a2dp_aac_t *aac, uint8_t size)
{
- unsigned freq = AAC_GET_FREQUENCY(*aac);
- unsigned bitrate = AAC_GET_BITRATE(*aac);
+ unsigned int freq, bitrate;
+
+ if (size < sizeof(*aac)) {
+ printf("\tMedia Codec: MPEG24 (broken)\n");
+ return;
+ }
+
+ freq = AAC_GET_FREQUENCY(*aac);
+ bitrate = AAC_GET_BITRATE(*aac);
printf("\tMedia Codec: MPEG24\n\t\tObject Types: ");
@@ -270,8 +295,13 @@ static void print_mpeg24(a2dp_aac_t *aac)
printf("\n\t\tVBR: %s", aac->vbr ? "Yes\n" : "No\n");
}
-static void print_mpeg12(a2dp_mpeg_t *mpeg)
+static void print_mpeg12(a2dp_mpeg_t *mpeg, uint8_t size)
{
+ if (size < sizeof(*mpeg)) {
+ printf("\tMedia Codec: MPEG12 (broken)\n");
+ return;
+ }
+
printf("\tMedia Codec: MPEG12\n\t\tChannel Modes: ");
if (mpeg->channel_mode & MPEG_CHANNEL_MODE_MONO)
@@ -351,8 +381,13 @@ static void print_mpeg12(a2dp_mpeg_t *mpeg)
printf("RFC-2250\n");
}
-static void print_sbc(a2dp_sbc_t *sbc)
+static void print_sbc(a2dp_sbc_t *sbc, uint8_t size)
{
+ if (size < sizeof(*sbc)) {
+ printf("\tMedia Codec: SBC (broken)\n");
+ return;
+ }
+
printf("\tMedia Codec: SBC\n\t\tChannel Modes: ");
if (sbc->channel_mode & SBC_CHANNEL_MODE_MONO)
@@ -394,20 +429,27 @@ static void print_sbc(a2dp_sbc_t *sbc)
sbc->min_bitpool, sbc->max_bitpool);
}
-static void print_media_codec(struct avdtp_media_codec_capability *cap)
+static void print_media_codec(
+ struct avdtp_media_codec_capability *cap,
+ uint8_t size)
{
+ if (size < sizeof(*cap)) {
+ printf("\tMedia Codec: Unknown (broken)\n");
+ return;
+ }
+
switch (cap->media_codec_type) {
case A2DP_CODEC_SBC:
- print_sbc((void *) cap->data);
+ print_sbc((void *) cap->data, size - 2);
break;
case A2DP_CODEC_MPEG12:
- print_mpeg12((void *) cap->data);
+ print_mpeg12((void *) cap->data, size - 2);
break;
case A2DP_CODEC_MPEG24:
- print_mpeg24((void *) cap->data);
+ print_mpeg24((void *) cap->data, size - 2);
break;
case A2DP_CODEC_VENDOR:
- print_vendor((void *) cap->data);
+ print_vendor((void *) cap->data, size - 2);
break;
default:
printf("\tMedia Codec: Unknown\n");
@@ -415,10 +457,16 @@ static void print_media_codec(struct avdtp_media_codec_capability *cap)
}
static void print_content_protection(
- struct avdtp_content_protection_capability *cap)
+ struct avdtp_content_protection_capability *cap,
+ uint8_t size)
{
printf("\tContent Protection: ");
+ if (size < sizeof(*cap)) {
+ printf("Unknown (broken)\n");
+ return;
+ }
+
switch (btohs(cap->content_protection_type)) {
case AVDTP_CONTENT_PROTECTION_TYPE_DTCP:
printf("DTCP");
@@ -452,13 +500,16 @@ static void print_caps(void *data, int size)
case AVDTP_REPORTING:
case AVDTP_RECOVERY:
case AVDTP_MULTIPLEXING:
+ default:
/* FIXME: Add proper functions */
+ printf("\tUnknown category: %d\n", cap->category);
break;
case AVDTP_MEDIA_CODEC:
- print_media_codec((void *) cap->data);
+ print_media_codec((void *) cap->data, cap->length);
break;
case AVDTP_CONTENT_PROTECTION:
- print_content_protection((void *) cap->data);
+ print_content_protection((void *) cap->data,
+ cap->length);
break;
}
--
2.21.0
From 5ac3a1beaffcd184767fb767b131375976e7a5d9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sun, 23 Dec 2018 11:40:13 +0100
Subject: [PATCH 02/31] avinfo: Show Vendor Specific Data
---
tools/avinfo.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/tools/avinfo.c b/tools/avinfo.c
index 47fa1d2c5..61bcdab0b 100644
--- a/tools/avinfo.c
+++ b/tools/avinfo.c
@@ -214,6 +214,7 @@ static void print_vendor(a2dp_vendor_codec_t *vendor, uint8_t size)
{
uint32_t vendor_id;
uint16_t codec_id;
+ int i;
if (size < sizeof(*vendor)) {
printf("\tMedia Codec: Vendor Specific A2DP Codec (broken)");
@@ -227,7 +228,12 @@ static void print_vendor(a2dp_vendor_codec_t *vendor, uint8_t size)
printf("\n\t\tVendor ID 0x%08x", vendor_id);
- printf("\n\t\tVendor Specific Codec ID 0x%04x\n", codec_id);
+ printf("\n\t\tVendor Specific Codec ID 0x%04x", codec_id);
+
+ printf("\n\t\tVendor Specific Data:");
+ for (i = 6; i < size; ++i)
+ printf(" 0x%.02x", ((unsigned char *)vendor)[i]);
+ printf("\n");
if (vendor_id == APTX_VENDOR_ID && codec_id == APTX_CODEC_ID)
print_aptx((void *) vendor, size);
--
2.21.0
From 51da4ed2a5b7cd05032faf5d7a695eecaf79f0f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sun, 23 Dec 2018 11:40:14 +0100
Subject: [PATCH 03/31] a2dp-codecs: Add SBC prefix for MIN/MAX_BITPOOL
constants
Those two constants are SBC codec specific.
---
android/hal-audio-sbc.c | 12 ++++++------
profiles/audio/a2dp-codecs.h | 4 ++--
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/android/hal-audio-sbc.c b/android/hal-audio-sbc.c
index 7ad3a6a51..fd6c61b95 100644
--- a/android/hal-audio-sbc.c
+++ b/android/hal-audio-sbc.c
@@ -90,8 +90,8 @@ static const a2dp_sbc_t sbc_presets[] = {
SBC_ALLOCATION_LOUDNESS,
.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16,
- .min_bitpool = MIN_BITPOOL,
- .max_bitpool = MAX_BITPOOL
+ .min_bitpool = SBC_MIN_BITPOOL,
+ .max_bitpool = SBC_MAX_BITPOOL
},
{
.frequency = SBC_SAMPLING_FREQ_44100,
@@ -99,8 +99,8 @@ static const a2dp_sbc_t sbc_presets[] = {
.subbands = SBC_SUBBANDS_8,
.allocation_method = SBC_ALLOCATION_LOUDNESS,
.block_length = SBC_BLOCK_LENGTH_16,
- .min_bitpool = MIN_BITPOOL,
- .max_bitpool = MAX_BITPOOL
+ .min_bitpool = SBC_MIN_BITPOOL,
+ .max_bitpool = SBC_MAX_BITPOOL
},
{
.frequency = SBC_SAMPLING_FREQ_48000,
@@ -108,8 +108,8 @@ static const a2dp_sbc_t sbc_presets[] = {
.subbands = SBC_SUBBANDS_8,
.allocation_method = SBC_ALLOCATION_LOUDNESS,
.block_length = SBC_BLOCK_LENGTH_16,
- .min_bitpool = MIN_BITPOOL,
- .max_bitpool = MAX_BITPOOL
+ .min_bitpool = SBC_MIN_BITPOOL,
+ .max_bitpool = SBC_MAX_BITPOOL
},
};
diff --git a/profiles/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h
index 4fb5c0cc9..205491939 100644
--- a/profiles/audio/a2dp-codecs.h
+++ b/profiles/audio/a2dp-codecs.h
@@ -49,8 +49,8 @@
#define SBC_ALLOCATION_SNR (1 << 1)
#define SBC_ALLOCATION_LOUDNESS 1
-#define MAX_BITPOOL 64
-#define MIN_BITPOOL 2
+#define SBC_MAX_BITPOOL 64
+#define SBC_MIN_BITPOOL 2
#define MPEG_CHANNEL_MODE_MONO (1 << 3)
#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
--
2.21.0
From b199f360f0c99337544fb6f5479c006f377988db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sun, 23 Dec 2018 11:40:15 +0100
Subject: [PATCH 04/31] a2dp-codecs: Fix codec id for ATRAC
According to Bluetooth Assigned Numbers for Audio/Video ATRAC has codec id 0x04.
See: https://www.bluetooth.com/specifications/assigned-numbers/audio-video
---
profiles/audio/a2dp-codecs.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/profiles/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h
index 205491939..25959902c 100644
--- a/profiles/audio/a2dp-codecs.h
+++ b/profiles/audio/a2dp-codecs.h
@@ -25,7 +25,7 @@
#define A2DP_CODEC_SBC 0x00
#define A2DP_CODEC_MPEG12 0x01
#define A2DP_CODEC_MPEG24 0x02
-#define A2DP_CODEC_ATRAC 0x03
+#define A2DP_CODEC_ATRAC 0x04
#define A2DP_CODEC_VENDOR 0xFF
#define SBC_SAMPLING_FREQ_16000 (1 << 3)
--
2.21.0
From dfa516c37d8ab07e4ba0f85289e2fa3246e89f36 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sun, 23 Dec 2018 11:40:16 +0100
Subject: [PATCH 05/31] a2dp-codecs & avinfo: Fix parsing MPEG bit rate values
Redefine bitrate field in a2dp_mpeg_t struct in endian neutral way and
separate vbr field according to A2DP specification. Define new macros
MPEG_GET_BITRATE() and MPEG_SET_BITRATE() for manipulating with bitrate
like for a2dp_aac_t struct.
And fix meaning of bitrate field. According to A2DP specification, it is
bitrate index, not bitrate itself. According to MPEG specification, each
MPEG layer have different bitrates for bitrate indexes. Therefore define
correctly bitrates for Layers 1, 2 and 3.
This fixes problems with parsing bitrate field in a2dp_mpeg_t struct as it
was broken due to endianity and it was broken for Layer 1 and 2 as bitrate
definitions was for Layer 3.
---
profiles/audio/a2dp-codecs.h | 93 +++++++++++++++++++-----
tools/avinfo.c | 135 ++++++++++++++++++++++++++---------
2 files changed, 176 insertions(+), 52 deletions(-)
diff --git a/profiles/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h
index 25959902c..47030bcc1 100644
--- a/profiles/audio/a2dp-codecs.h
+++ b/profiles/audio/a2dp-codecs.h
@@ -68,22 +68,75 @@
#define MPEG_SAMPLING_FREQ_44100 (1 << 1)
#define MPEG_SAMPLING_FREQ_48000 1
-#define MPEG_BIT_RATE_VBR 0x8000
-#define MPEG_BIT_RATE_320000 0x4000
-#define MPEG_BIT_RATE_256000 0x2000
-#define MPEG_BIT_RATE_224000 0x1000
-#define MPEG_BIT_RATE_192000 0x0800
-#define MPEG_BIT_RATE_160000 0x0400
-#define MPEG_BIT_RATE_128000 0x0200
-#define MPEG_BIT_RATE_112000 0x0100
-#define MPEG_BIT_RATE_96000 0x0080
-#define MPEG_BIT_RATE_80000 0x0040
-#define MPEG_BIT_RATE_64000 0x0020
-#define MPEG_BIT_RATE_56000 0x0010
-#define MPEG_BIT_RATE_48000 0x0008
-#define MPEG_BIT_RATE_40000 0x0004
-#define MPEG_BIT_RATE_32000 0x0002
-#define MPEG_BIT_RATE_FREE 0x0001
+#define MPEG_BIT_RATE_INDEX_0 (1 << 0)
+#define MPEG_BIT_RATE_INDEX_1 (1 << 1)
+#define MPEG_BIT_RATE_INDEX_2 (1 << 2)
+#define MPEG_BIT_RATE_INDEX_3 (1 << 3)
+#define MPEG_BIT_RATE_INDEX_4 (1 << 4)
+#define MPEG_BIT_RATE_INDEX_5 (1 << 5)
+#define MPEG_BIT_RATE_INDEX_6 (1 << 6)
+#define MPEG_BIT_RATE_INDEX_7 (1 << 7)
+#define MPEG_BIT_RATE_INDEX_8 (1 << 8)
+#define MPEG_BIT_RATE_INDEX_9 (1 << 9)
+#define MPEG_BIT_RATE_INDEX_10 (1 << 10)
+#define MPEG_BIT_RATE_INDEX_11 (1 << 11)
+#define MPEG_BIT_RATE_INDEX_12 (1 << 12)
+#define MPEG_BIT_RATE_INDEX_13 (1 << 13)
+#define MPEG_BIT_RATE_INDEX_14 (1 << 14)
+
+#define MPEG_MP1_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1
+#define MPEG_MP1_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_2
+#define MPEG_MP1_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_3
+#define MPEG_MP1_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_4
+#define MPEG_MP1_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_5
+#define MPEG_MP1_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_6
+#define MPEG_MP1_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_7
+#define MPEG_MP1_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_8
+#define MPEG_MP1_BIT_RATE_288000 MPEG_BIT_RATE_INDEX_9
+#define MPEG_MP1_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_10
+#define MPEG_MP1_BIT_RATE_352000 MPEG_BIT_RATE_INDEX_11
+#define MPEG_MP1_BIT_RATE_384000 MPEG_BIT_RATE_INDEX_12
+#define MPEG_MP1_BIT_RATE_416000 MPEG_BIT_RATE_INDEX_13
+#define MPEG_MP1_BIT_RATE_448000 MPEG_BIT_RATE_INDEX_14
+
+#define MPEG_MP2_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1
+#define MPEG_MP2_BIT_RATE_48000 MPEG_BIT_RATE_INDEX_2
+#define MPEG_MP2_BIT_RATE_56000 MPEG_BIT_RATE_INDEX_3
+#define MPEG_MP2_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_4
+#define MPEG_MP2_BIT_RATE_80000 MPEG_BIT_RATE_INDEX_5
+#define MPEG_MP2_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_6
+#define MPEG_MP2_BIT_RATE_112000 MPEG_BIT_RATE_INDEX_7
+#define MPEG_MP2_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_8
+#define MPEG_MP2_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_9
+#define MPEG_MP2_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_10
+#define MPEG_MP2_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_11
+#define MPEG_MP2_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_12
+#define MPEG_MP2_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_13
+#define MPEG_MP2_BIT_RATE_384000 MPEG_BIT_RATE_INDEX_14
+
+#define MPEG_MP3_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1
+#define MPEG_MP3_BIT_RATE_40000 MPEG_BIT_RATE_INDEX_2
+#define MPEG_MP3_BIT_RATE_48000 MPEG_BIT_RATE_INDEX_3
+#define MPEG_MP3_BIT_RATE_56000 MPEG_BIT_RATE_INDEX_4
+#define MPEG_MP3_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_5
+#define MPEG_MP3_BIT_RATE_80000 MPEG_BIT_RATE_INDEX_6
+#define MPEG_MP3_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_7
+#define MPEG_MP3_BIT_RATE_112000 MPEG_BIT_RATE_INDEX_8
+#define MPEG_MP3_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_9
+#define MPEG_MP3_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_10
+#define MPEG_MP3_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_11
+#define MPEG_MP3_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_12
+#define MPEG_MP3_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_13
+#define MPEG_MP3_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_14
+
+#define MPEG_BIT_RATE_FREE MPEG_BIT_RATE_INDEX_0
+
+#define MPEG_GET_BITRATE(a) ((uint16_t)(a).bitrate1 << 8 | (a).bitrate2)
+#define MPEG_SET_BITRATE(a, b) \
+ do { \
+ (a).bitrate1 = ((b) >> 8) & 0x7f; \
+ (a).bitrate2 = (b) & 0xff; \
+ } while (0)
#define AAC_OBJECT_TYPE_MPEG2_AAC_LC 0x80
#define AAC_OBJECT_TYPE_MPEG4_AAC_LC 0x40
@@ -168,7 +221,9 @@ typedef struct {
uint8_t frequency:6;
uint8_t mpf:1;
uint8_t rfa:1;
- uint16_t bitrate;
+ uint8_t bitrate1:7;
+ uint8_t vbr:1;
+ uint8_t bitrate2;
} __attribute__ ((packed)) a2dp_mpeg_t;
typedef struct {
@@ -213,7 +268,9 @@ typedef struct {
uint8_t rfa:1;
uint8_t mpf:1;
uint8_t frequency:6;
- uint16_t bitrate;
+ uint8_t vbr:1;
+ uint8_t bitrate1:7;
+ uint8_t bitrate2;
} __attribute__ ((packed)) a2dp_mpeg_t;
typedef struct {
diff --git a/tools/avinfo.c b/tools/avinfo.c
index 61bcdab0b..2398cc5e0 100644
--- a/tools/avinfo.c
+++ b/tools/avinfo.c
@@ -303,11 +303,15 @@ static void print_mpeg24(a2dp_aac_t *aac, uint8_t size)
static void print_mpeg12(a2dp_mpeg_t *mpeg, uint8_t size)
{
+ uint16_t bitrate;
+
if (size < sizeof(*mpeg)) {
printf("\tMedia Codec: MPEG12 (broken)\n");
return;
}
+ bitrate = MPEG_GET_BITRATE(*mpeg);
+
printf("\tMedia Codec: MPEG12\n\t\tChannel Modes: ");
if (mpeg->channel_mode & MPEG_CHANNEL_MODE_MONO)
@@ -343,42 +347,105 @@ static void print_mpeg12(a2dp_mpeg_t *mpeg, uint8_t size)
if (mpeg->layer & MPEG_LAYER_MP3)
printf("3 ");
- printf("\n\t\tBit Rate: ");
- if (mpeg->bitrate & MPEG_BIT_RATE_FREE)
- printf("Free format");
- else {
- if (mpeg->bitrate & MPEG_BIT_RATE_32000)
- printf("32kbps ");
- if (mpeg->bitrate & MPEG_BIT_RATE_40000)
- printf("40kbps ");
- if (mpeg->bitrate & MPEG_BIT_RATE_48000)
- printf("48kbps ");
- if (mpeg->bitrate & MPEG_BIT_RATE_56000)
- printf("56kbps ");
- if (mpeg->bitrate & MPEG_BIT_RATE_64000)
- printf("64kbps ");
- if (mpeg->bitrate & MPEG_BIT_RATE_80000)
- printf("80kbps ");
- if (mpeg->bitrate & MPEG_BIT_RATE_96000)
- printf("96kbps ");
- if (mpeg->bitrate & MPEG_BIT_RATE_112000)
- printf("112kbps ");
- if (mpeg->bitrate & MPEG_BIT_RATE_128000)
- printf("128kbps ");
- if (mpeg->bitrate & MPEG_BIT_RATE_160000)
- printf("160kbps ");
- if (mpeg->bitrate & MPEG_BIT_RATE_192000)
- printf("192kbps ");
- if (mpeg->bitrate & MPEG_BIT_RATE_224000)
- printf("224kbps ");
- if (mpeg->bitrate & MPEG_BIT_RATE_256000)
- printf("256kbps ");
- if (mpeg->bitrate & MPEG_BIT_RATE_320000)
- printf("320kbps ");
+ if (bitrate & MPEG_BIT_RATE_FREE) {
+ printf("\n\t\tBit Rate: Free format");
+ } else {
+ if (mpeg->layer & MPEG_LAYER_MP1) {
+ printf("\n\t\tLayer 1 Bit Rate: ");
+ if (bitrate & MPEG_MP1_BIT_RATE_32000)
+ printf("32kbps ");
+ if (bitrate & MPEG_MP1_BIT_RATE_64000)
+ printf("64kbps ");
+ if (bitrate & MPEG_MP1_BIT_RATE_96000)
+ printf("96kbps ");
+ if (bitrate & MPEG_MP1_BIT_RATE_128000)
+ printf("128kbps ");
+ if (bitrate & MPEG_MP1_BIT_RATE_160000)
+ printf("160kbps ");
+ if (bitrate & MPEG_MP1_BIT_RATE_192000)
+ printf("192kbps ");
+ if (bitrate & MPEG_MP1_BIT_RATE_224000)
+ printf("224kbps ");
+ if (bitrate & MPEG_MP1_BIT_RATE_256000)
+ printf("256kbps ");
+ if (bitrate & MPEG_MP1_BIT_RATE_320000)
+ printf("320kbps ");
+ if (bitrate & MPEG_MP1_BIT_RATE_352000)
+ printf("352kbps ");
+ if (bitrate & MPEG_MP1_BIT_RATE_384000)
+ printf("384kbps ");
+ if (bitrate & MPEG_MP1_BIT_RATE_416000)
+ printf("416kbps ");
+ if (bitrate & MPEG_MP1_BIT_RATE_448000)
+ printf("448kbps ");
+ }
+
+ if (mpeg->layer & MPEG_LAYER_MP2) {
+ printf("\n\t\tLayer 2 Bit Rate: ");
+ if (bitrate & MPEG_MP2_BIT_RATE_32000)
+ printf("32kbps ");
+ if (bitrate & MPEG_MP2_BIT_RATE_48000)
+ printf("48kbps ");
+ if (bitrate & MPEG_MP2_BIT_RATE_56000)
+ printf("56kbps ");
+ if (bitrate & MPEG_MP2_BIT_RATE_64000)
+ printf("64kbps ");
+ if (bitrate & MPEG_MP2_BIT_RATE_80000)
+ printf("80kbps ");
+ if (bitrate & MPEG_MP2_BIT_RATE_96000)
+ printf("96kbps ");
+ if (bitrate & MPEG_MP2_BIT_RATE_112000)
+ printf("112kbps ");
+ if (bitrate & MPEG_MP2_BIT_RATE_128000)
+ printf("128kbps ");
+ if (bitrate & MPEG_MP2_BIT_RATE_160000)
+ printf("160kbps ");
+ if (bitrate & MPEG_MP2_BIT_RATE_192000)
+ printf("192kbps ");
+ if (bitrate & MPEG_MP2_BIT_RATE_224000)
+ printf("224kbps ");
+ if (bitrate & MPEG_MP2_BIT_RATE_256000)
+ printf("256kbps ");
+ if (bitrate & MPEG_MP2_BIT_RATE_320000)
+ printf("320kbps ");
+ if (bitrate & MPEG_MP2_BIT_RATE_384000)
+ printf("384kbps ");
+ }
+
+ if (mpeg->layer & MPEG_LAYER_MP3) {
+ printf("\n\t\tLayer 3 Bit Rate: ");
+ if (bitrate & MPEG_MP3_BIT_RATE_32000)
+ printf("32kbps ");
+ if (bitrate & MPEG_MP3_BIT_RATE_40000)
+ printf("40kbps ");
+ if (bitrate & MPEG_MP3_BIT_RATE_48000)
+ printf("48kbps ");
+ if (bitrate & MPEG_MP3_BIT_RATE_56000)
+ printf("56kbps ");
+ if (bitrate & MPEG_MP3_BIT_RATE_64000)
+ printf("64kbps ");
+ if (bitrate & MPEG_MP3_BIT_RATE_80000)
+ printf("80kbps ");
+ if (bitrate & MPEG_MP3_BIT_RATE_96000)
+ printf("96kbps ");
+ if (bitrate & MPEG_MP3_BIT_RATE_112000)
+ printf("112kbps ");
+ if (bitrate & MPEG_MP3_BIT_RATE_128000)
+ printf("128kbps ");
+ if (bitrate & MPEG_MP3_BIT_RATE_160000)
+ printf("160kbps ");
+ if (bitrate & MPEG_MP3_BIT_RATE_192000)
+ printf("192kbps ");
+ if (bitrate & MPEG_MP3_BIT_RATE_224000)
+ printf("224kbps ");
+ if (bitrate & MPEG_MP3_BIT_RATE_256000)
+ printf("256kbps ");
+ if (bitrate & MPEG_MP3_BIT_RATE_320000)
+ printf("320kbps ");
+ }
}
- printf("\n\t\tVBR: %s", mpeg->bitrate & MPEG_BIT_RATE_VBR ? "Yes" :
- "No");
+ printf("\n\t\tVBR: %s", mpeg->vbr ? "Yes" : "No");
printf("\n\t\tPayload Format: ");
if (mpeg->mpf)
--
2.21.0
From 70874281220368ed24bb99e916b765608c01a44a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sun, 23 Dec 2018 11:40:17 +0100
Subject: [PATCH 06/31] a2dp-codecs: Define a2dp_vendor_codec_t struct in
endian neutral way
And define new macros A2DP_GET_VENDOR_ID(), A2DP_GET_CODEC_ID() and
A2DP_SET_VENDOR_ID_CODEC_ID() for easily filling a2dp_vendor_codec_t
struct.
---
android/a2dp.c | 8 ++++----
android/avdtp.c | 6 ++++--
android/hal-audio-aptx.c | 18 ++++++------------
profiles/audio/a2dp-codecs.h | 24 ++++++++++++++++++++++--
profiles/audio/a2dp.c | 9 +++++----
tools/avinfo.c | 4 ++--
6 files changed, 43 insertions(+), 26 deletions(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index f21904208..8bcdfd20f 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -417,8 +417,8 @@ static int check_capabilities(struct a2dp_preset *preset,
preset->len);
case A2DP_CODEC_VENDOR:
vndcodec = (void *) codec->data;
- if (btohl(vndcodec->vendor_id) == APTX_VENDOR_ID &&
- btohs(vndcodec->codec_id) == APTX_CODEC_ID)
+ if (A2DP_GET_VENDOR_ID(*vndcodec) == APTX_VENDOR_ID &&
+ A2DP_GET_CODEC_ID(*vndcodec) == APTX_CODEC_ID)
return aptx_check_config(codec->data, codec_len,
preset->data, preset->len);
return -EINVAL;
@@ -1344,8 +1344,8 @@ static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
a2dp_vendor_codec_t *vndcodec = (void *) endpoint->caps->data;
avdtp_sep_set_vendor_codec(endpoint->sep,
- btohl(vndcodec->vendor_id),
- btohs(vndcodec->codec_id));
+ A2DP_GET_VENDOR_ID(*vndcodec),
+ A2DP_GET_CODEC_ID(*vndcodec));
}
endpoints = g_slist_append(endpoints, endpoint);
diff --git a/android/avdtp.c b/android/avdtp.c
index 34caf3db5..7fb8cb731 100644
--- a/android/avdtp.c
+++ b/android/avdtp.c
@@ -1103,10 +1103,12 @@ struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
a2dp_vendor_codec_t *vndcodec =
(void *) codec_data->data;
- if (btohl(vndcodec->vendor_id) != lsep->vndcodec_vendor)
+ if (A2DP_GET_VENDOR_ID(*vndcodec) !=
+ lsep->vndcodec_vendor)
continue;
- if (btohs(vndcodec->codec_id) != lsep->vndcodec_codec)
+ if (A2DP_GET_CODEC_ID(*vndcodec) !=
+ lsep->vndcodec_codec)
continue;
}
diff --git a/android/hal-audio-aptx.c b/android/hal-audio-aptx.c
index a8000759b..4707f593e 100644
--- a/android/hal-audio-aptx.c
+++ b/android/hal-audio-aptx.c
@@ -36,27 +36,21 @@ struct aptx_data {
static const a2dp_aptx_t aptx_presets[] = {
{
- .info = {
- .vendor_id = APTX_VENDOR_ID,
- .codec_id = APTX_CODEC_ID,
- },
+ .info =
+ A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID),
.frequency = APTX_SAMPLING_FREQ_44100 |
APTX_SAMPLING_FREQ_48000,
.channel_mode = APTX_CHANNEL_MODE_STEREO,
},
{
- .info = {
- .vendor_id = APTX_VENDOR_ID,
- .codec_id = APTX_CODEC_ID,
- },
+ .info =
+ A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID),
.frequency = APTX_SAMPLING_FREQ_48000,
.channel_mode = APTX_CHANNEL_MODE_STEREO,
},
{
- .info = {
- .vendor_id = APTX_VENDOR_ID,
- .codec_id = APTX_CODEC_ID,
- },
+ .info =
+ A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID),
.frequency = APTX_SAMPLING_FREQ_44100,
.channel_mode = APTX_CHANNEL_MODE_STEREO,
},
diff --git a/profiles/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h
index 47030bcc1..a310efe49 100644
--- a/profiles/audio/a2dp-codecs.h
+++ b/profiles/audio/a2dp-codecs.h
@@ -198,10 +198,30 @@
#define LDAC_CODEC_ID 0x00aa
typedef struct {
- uint32_t vendor_id;
- uint16_t codec_id;
+ uint8_t vendor_id4;
+ uint8_t vendor_id3;
+ uint8_t vendor_id2;
+ uint8_t vendor_id1;
+ uint8_t codec_id2;
+ uint8_t codec_id1;
} __attribute__ ((packed)) a2dp_vendor_codec_t;
+#define A2DP_GET_VENDOR_ID(a) ( \
+ (((uint32_t)(a).vendor_id4) << 0) | \
+ (((uint32_t)(a).vendor_id3) << 8) | \
+ (((uint32_t)(a).vendor_id2) << 16) | \
+ (((uint32_t)(a).vendor_id1) << 24) \
+ )
+#define A2DP_GET_CODEC_ID(a) ((a).codec_id2 | (((uint16_t)(a).codec_id1) << 8))
+#define A2DP_SET_VENDOR_ID_CODEC_ID(v, c) ((a2dp_vendor_codec_t){ \
+ .vendor_id4 = (((v) >> 0) & 0xff), \
+ .vendor_id3 = (((v) >> 8) & 0xff), \
+ .vendor_id2 = (((v) >> 16) & 0xff), \
+ .vendor_id1 = (((v) >> 24) & 0xff), \
+ .codec_id2 = (((c) >> 0) & 0xff), \
+ .codec_id1 = (((c) >> 8) & 0xff), \
+ })
+
#if __BYTE_ORDER == __LITTLE_ENDIAN
typedef struct {
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index fc98bb264..344459332 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -523,14 +523,15 @@ static gboolean endpoint_match_codec_ind(struct avdtp *session,
local_codec = (a2dp_vendor_codec_t *) capabilities;
remote_codec = (a2dp_vendor_codec_t *) codec->data;
- if (remote_codec->vendor_id != local_codec->vendor_id)
+ if (A2DP_GET_VENDOR_ID(*remote_codec) !=
+ A2DP_GET_VENDOR_ID(*local_codec))
return FALSE;
- if (remote_codec->codec_id != local_codec->codec_id)
+ if (A2DP_GET_CODEC_ID(*remote_codec) != A2DP_GET_CODEC_ID(*local_codec))
return FALSE;
- DBG("vendor 0x%08x codec 0x%04x", btohl(remote_codec->vendor_id),
- btohs(remote_codec->codec_id));
+ DBG("vendor 0x%08x codec 0x%04x", A2DP_GET_VENDOR_ID(*remote_codec),
+ A2DP_GET_CODEC_ID(*remote_codec));
return TRUE;
}
diff --git a/tools/avinfo.c b/tools/avinfo.c
index 2398cc5e0..424221f8d 100644
--- a/tools/avinfo.c
+++ b/tools/avinfo.c
@@ -221,8 +221,8 @@ static void print_vendor(a2dp_vendor_codec_t *vendor, uint8_t size)
return;
}
- vendor_id = btohl(vendor->vendor_id);
- codec_id = btohs(vendor->codec_id);
+ vendor_id = A2DP_GET_VENDOR_ID(*vendor);
+ codec_id = A2DP_GET_CODEC_ID(*vendor);
printf("\tMedia Codec: Vendor Specific A2DP Codec");
--
2.21.0
From f20550833ec0a3fee5930cc25db0b51f8492f65a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sun, 23 Dec 2018 11:40:18 +0100
Subject: [PATCH 07/31] a2dp-codecs: Add needed includes and properly check for
endian macros
Without stdint.h type uint8_t cannot be used.
And without endian.h macros __BYTE_ORDER, __LITTLE_ENDIAN and __BIG_ENDIAN
are not defined.
When both __BYTE_ORDER and __LITTLE_ENDIAN macros are not defined, then
condition #if __BYTE_ORDER == __LITTLE_ENDIAN is true.
---
profiles/audio/a2dp-codecs.h | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/profiles/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h
index a310efe49..649e2411b 100644
--- a/profiles/audio/a2dp-codecs.h
+++ b/profiles/audio/a2dp-codecs.h
@@ -22,6 +22,9 @@
*
*/
+#include <endian.h>
+#include <stdint.h>
+
#define A2DP_CODEC_SBC 0x00
#define A2DP_CODEC_MPEG12 0x01
#define A2DP_CODEC_MPEG24 0x02
@@ -222,7 +225,8 @@ typedef struct {
.codec_id1 = (((c) >> 8) & 0xff), \
})
-#if __BYTE_ORDER == __LITTLE_ENDIAN
+#if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+ __BYTE_ORDER == __LITTLE_ENDIAN
typedef struct {
uint8_t channel_mode:4;
@@ -269,7 +273,8 @@ typedef struct {
uint8_t unknown[2];
} __attribute__ ((packed)) a2dp_ldac_t;
-#elif __BYTE_ORDER == __BIG_ENDIAN
+#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
+ __BYTE_ORDER == __BIG_ENDIAN
typedef struct {
uint8_t frequency:4;
--
2.21.0
From 99c71523e1d4b33885d2e095f3a7539764edbce8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sun, 23 Dec 2018 11:40:19 +0100
Subject: [PATCH 08/31] a2dp-codecs: Properly define macros and struct for LDAC
codec
---
profiles/audio/a2dp-codecs.h | 27 +++++++++++++++++----------
tools/avinfo.c | 4 ++--
2 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/profiles/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h
index 649e2411b..6f667d3aa 100644
--- a/profiles/audio/a2dp-codecs.h
+++ b/profiles/audio/a2dp-codecs.h
@@ -200,6 +200,17 @@
#define LDAC_VENDOR_ID 0x0000012d
#define LDAC_CODEC_ID 0x00aa
+#define LDAC_SAMPLING_FREQ_44100 0x20
+#define LDAC_SAMPLING_FREQ_48000 0x10
+#define LDAC_SAMPLING_FREQ_88200 0x08
+#define LDAC_SAMPLING_FREQ_96000 0x04
+#define LDAC_SAMPLING_FREQ_176400 0x02
+#define LDAC_SAMPLING_FREQ_192000 0x01
+
+#define LDAC_CHANNEL_MODE_MONO 0x04
+#define LDAC_CHANNEL_MODE_DUAL 0x02
+#define LDAC_CHANNEL_MODE_STEREO 0x01
+
typedef struct {
uint8_t vendor_id4;
uint8_t vendor_id3;
@@ -225,6 +236,12 @@ typedef struct {
.codec_id1 = (((c) >> 8) & 0xff), \
})
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t frequency;
+ uint8_t channel_mode;
+} __attribute__ ((packed)) a2dp_ldac_t;
+
#if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
__BYTE_ORDER == __LITTLE_ENDIAN
@@ -268,11 +285,6 @@ typedef struct {
uint8_t frequency:4;
} __attribute__ ((packed)) a2dp_aptx_t;
-typedef struct {
- a2dp_vendor_codec_t info;
- uint8_t unknown[2];
-} __attribute__ ((packed)) a2dp_ldac_t;
-
#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
__BYTE_ORDER == __BIG_ENDIAN
@@ -316,11 +328,6 @@ typedef struct {
uint8_t channel_mode:4;
} __attribute__ ((packed)) a2dp_aptx_t;
-typedef struct {
- a2dp_vendor_codec_t info;
- uint8_t unknown[2];
-} __attribute__ ((packed)) a2dp_ldac_t;
-
#else
#error "Unknown byte order"
#endif
diff --git a/tools/avinfo.c b/tools/avinfo.c
index 424221f8d..1852b2473 100644
--- a/tools/avinfo.c
+++ b/tools/avinfo.c
@@ -204,8 +204,8 @@ static void print_ldac(a2dp_ldac_t *ldac, uint8_t size)
return;
}
- printf("\n\t\t\tUnknown: %02x %02x", ldac->unknown[0],
- ldac->unknown[1]);
+ printf("\n\t\t\tUnknown: %02x %02x", ldac->frequency,
+ ldac->channel_mode);
printf("\n");
}
--
2.21.0
From 8e119a083b931a53d19ba218973c31bdda56138d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sun, 23 Dec 2018 11:40:20 +0100
Subject: [PATCH 09/31] a2dp-codecs: Add macros and structures for new codecs
Add additional codecs: FastStream, aptX Low Latency and aptX HD codecs.
---
profiles/audio/a2dp-codecs.h | 120 +++++++++++++++++++++++++++++++++++
1 file changed, 120 insertions(+)
diff --git a/profiles/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h
index 6f667d3aa..0bdd29110 100644
--- a/profiles/audio/a2dp-codecs.h
+++ b/profiles/audio/a2dp-codecs.h
@@ -4,6 +4,7 @@
*
* Copyright (C) 2006-2010 Nokia Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2018 Pali Rohár <pali.rohar@gmail.com>
*
*
* This library is free software; you can redistribute it and/or
@@ -197,6 +198,59 @@
#define APTX_SAMPLING_FREQ_44100 0x02
#define APTX_SAMPLING_FREQ_48000 0x01
+#define FASTSTREAM_VENDOR_ID 0x0000000a
+#define FASTSTREAM_CODEC_ID 0x0001
+
+#define FASTSTREAM_DIRECTION_SINK 0x1
+#define FASTSTREAM_DIRECTION_SOURCE 0x2
+
+#define FASTSTREAM_SINK_SAMPLING_FREQ_44100 0x2
+#define FASTSTREAM_SINK_SAMPLING_FREQ_48000 0x1
+
+#define FASTSTREAM_SOURCE_SAMPLING_FREQ_16000 0x2
+
+#define APTX_LL_VENDOR_ID 0x0000000a
+#define APTX_LL_CODEC_ID 0x0002
+
+#define APTX_LL_CHANNEL_MODE_MONO 0x01
+#define APTX_LL_CHANNEL_MODE_STEREO 0x02
+
+#define APTX_LL_SAMPLING_FREQ_16000 0x08
+#define APTX_LL_SAMPLING_FREQ_32000 0x04
+#define APTX_LL_SAMPLING_FREQ_44100 0x02
+#define APTX_LL_SAMPLING_FREQ_48000 0x01
+
+/* Default parameters for aptX Low Latency encoder */
+
+/* Target codec buffer level = 180 */
+#define APTX_LL_TARGET_LEVEL2 0xb4
+#define APTX_LL_TARGET_LEVEL1 0x00
+
+/* Initial codec buffer level = 360 */
+#define APTX_LL_INITIAL_LEVEL2 0x68
+#define APTX_LL_INITIAL_LEVEL1 0x01
+
+/* SRA max rate 0.005 * 10000 = 50 */
+#define APTX_LL_SRA_MAX_RATE 0x32
+
+/* SRA averaging time = 1s */
+#define APTX_LL_SRA_AVG_TIME 0x01
+
+/* Good working codec buffer level = 180 */
+#define APTX_LL_GOOD_WORKING_LEVEL2 0xB4
+#define APTX_LL_GOOD_WORKING_LEVEL1 0x00
+
+#define APTX_HD_VENDOR_ID 0x000000D7
+#define APTX_HD_CODEC_ID 0x0024
+
+#define APTX_HD_CHANNEL_MODE_MONO 0x1
+#define APTX_HD_CHANNEL_MODE_STEREO 0x2
+
+#define APTX_HD_SAMPLING_FREQ_16000 0x8
+#define APTX_HD_SAMPLING_FREQ_32000 0x4
+#define APTX_HD_SAMPLING_FREQ_44100 0x2
+#define APTX_HD_SAMPLING_FREQ_48000 0x1
+
#define LDAC_VENDOR_ID 0x0000012d
#define LDAC_CODEC_ID 0x00aa
@@ -236,6 +290,18 @@ typedef struct {
.codec_id1 = (((c) >> 8) & 0xff), \
})
+typedef struct {
+ uint8_t reserved;
+ uint8_t target_level2;
+ uint8_t target_level1;
+ uint8_t initial_level2;
+ uint8_t initial_level1;
+ uint8_t sra_max_rate;
+ uint8_t sra_avg_time;
+ uint8_t good_working_level2;
+ uint8_t good_working_level1;
+} __attribute__ ((packed)) a2dp_aptx_ll_new_caps_t;
+
typedef struct {
a2dp_vendor_codec_t info;
uint8_t frequency;
@@ -285,6 +351,33 @@ typedef struct {
uint8_t frequency:4;
} __attribute__ ((packed)) a2dp_aptx_t;
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t direction;
+ uint8_t sink_frequency:4;
+ uint8_t source_frequency:4;
+} __attribute__ ((packed)) a2dp_faststream_t;
+
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t channel_mode:4;
+ uint8_t frequency:4;
+ uint8_t bidirect_link:1;
+ uint8_t has_new_caps:1;
+ uint8_t reserved:6;
+ a2dp_aptx_ll_new_caps_t new_caps[0];
+} __attribute__ ((packed)) a2dp_aptx_ll_t;
+
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t channel_mode:4;
+ uint8_t frequency:4;
+ uint8_t reserved0;
+ uint8_t reserved1;
+ uint8_t reserved2;
+ uint8_t reserved3;
+} __attribute__ ((packed)) a2dp_aptx_hd_t;
+
#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
__BYTE_ORDER == __BIG_ENDIAN
@@ -328,6 +421,33 @@ typedef struct {
uint8_t channel_mode:4;
} __attribute__ ((packed)) a2dp_aptx_t;
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t direction;
+ uint8_t source_frequency:4;
+ uint8_t sink_frequency:4;
+} __attribute__ ((packed)) a2dp_faststream_t;
+
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t frequency:4;
+ uint8_t channel_mode:4;
+ uint8_t reserved:6;
+ uint8_t has_new_caps:1;
+ uint8_t bidirect_link:1;
+ a2dp_aptx_ll_new_caps_t new_caps[0];
+} __attribute__ ((packed)) a2dp_aptx_ll_t;
+
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t frequency:4;
+ uint8_t channel_mode:4;
+ uint8_t reserved0;
+ uint8_t reserved1;
+ uint8_t reserved2;
+ uint8_t reserved3;
+} __attribute__ ((packed)) a2dp_aptx_hd_t;
+
#else
#error "Unknown byte order"
#endif
--
2.21.0
From 7a2ea44b4c9929507a679debf0cf74416146a5d0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sun, 23 Dec 2018 11:40:21 +0100
Subject: [PATCH 10/31] avinfo: Parse new A2DP codecs
Parse information about additional A2DP codecs: FastStream, aptX Low
Latency, aptX HD and LDAC.
---
tools/avinfo.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 144 insertions(+), 2 deletions(-)
diff --git a/tools/avinfo.c b/tools/avinfo.c
index 1852b2473..02fc1f233 100644
--- a/tools/avinfo.c
+++ b/tools/avinfo.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2006-2010 Nokia Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2018 Pali Rohár <pali.rohar@gmail.com>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -195,6 +196,121 @@ static void print_aptx(a2dp_aptx_t *aptx, uint8_t size)
printf("\n");
}
+static void print_faststream(a2dp_faststream_t *faststream, uint8_t size)
+{
+ printf("\t\tVendor Specific Value (FastStream)");
+
+ if (size < sizeof(*faststream)) {
+ printf(" (broken)\n");
+ return;
+ }
+
+ printf("\n\t\t\tDirections: ");
+ if (faststream->direction & FASTSTREAM_DIRECTION_SINK)
+ printf("sink ");
+ if (faststream->direction & FASTSTREAM_DIRECTION_SOURCE)
+ printf("source ");
+
+ if (faststream->direction & FASTSTREAM_DIRECTION_SINK) {
+ printf("\n\t\t\tSink Frequencies: ");
+ if (faststream->sink_frequency &
+ FASTSTREAM_SINK_SAMPLING_FREQ_44100)
+ printf("44.1kHz ");
+ if (faststream->sink_frequency &
+ FASTSTREAM_SINK_SAMPLING_FREQ_48000)
+ printf("48kHz ");
+ }
+
+ if (faststream->direction & FASTSTREAM_DIRECTION_SOURCE) {
+ printf("\n\t\t\tSource Frequencies: ");
+ if (faststream->source_frequency &
+ FASTSTREAM_SOURCE_SAMPLING_FREQ_16000)
+ printf("16kHz ");
+ }
+
+ printf("\n");
+}
+
+static void print_aptx_ll(a2dp_aptx_ll_t *aptx_ll, uint8_t size)
+{
+ a2dp_aptx_ll_new_caps_t *aptx_ll_new;
+
+ printf("\t\tVendor Specific Value (aptX Low Latency)");
+
+ if (size < sizeof(*aptx_ll)) {
+ printf(" (broken)\n");
+ return;
+ }
+
+ printf("\n\t\t\tFrequencies: ");
+ if (aptx_ll->frequency & APTX_LL_SAMPLING_FREQ_16000)
+ printf("16kHz ");
+ if (aptx_ll->frequency & APTX_LL_SAMPLING_FREQ_32000)
+ printf("32kHz ");
+ if (aptx_ll->frequency & APTX_LL_SAMPLING_FREQ_44100)
+ printf("44.1kHz ");
+ if (aptx_ll->frequency & APTX_LL_SAMPLING_FREQ_48000)
+ printf("48kHz ");
+
+ printf("\n\t\t\tChannel modes: ");
+ if (aptx_ll->channel_mode & APTX_LL_CHANNEL_MODE_MONO)
+ printf("Mono ");
+ if (aptx_ll->channel_mode & APTX_LL_CHANNEL_MODE_STEREO)
+ printf("Stereo ");
+
+ printf("\n\t\tBidirectional link: %s",
+ aptx_ll->bidirect_link ? "Yes" : "No");
+
+ aptx_ll_new = &aptx_ll->new_caps[0];
+ if (aptx_ll->has_new_caps &&
+ size >= sizeof(*aptx_ll) + sizeof(*aptx_ll_new)) {
+ printf("\n\t\tTarget codec buffer level: %u",
+ (unsigned int)aptx_ll_new->target_level2 |
+ ((unsigned int)(aptx_ll_new->target_level1) << 8));
+ printf("\n\t\tInitial codec buffer level: %u",
+ (unsigned int)aptx_ll_new->initial_level2 |
+ ((unsigned int)(aptx_ll_new->initial_level1) << 8));
+ printf("\n\t\tSRA max rate: %g",
+ aptx_ll_new->sra_max_rate / 10000.0);
+ printf("\n\t\tSRA averaging time: %us",
+ (unsigned int)aptx_ll_new->sra_avg_time);
+ printf("\n\t\tGood working codec buffer level: %u",
+ (unsigned int)aptx_ll_new->good_working_level2 |
+ ((unsigned int)(aptx_ll_new->good_working_level1) << 8)
+ );
+ }
+
+ printf("\n");
+}
+
+static void print_aptx_hd(a2dp_aptx_hd_t *aptx_hd, uint8_t size)
+{
+ printf("\t\tVendor Specific Value (aptX HD)");
+
+ if (size < sizeof(*aptx_hd)) {
+ printf(" (broken)\n");
+ return;
+ }
+
+ printf("\n\t\t\tFrequencies: ");
+ if (aptx_hd->frequency & APTX_HD_SAMPLING_FREQ_16000)
+ printf("16kHz ");
+ if (aptx_hd->frequency & APTX_HD_SAMPLING_FREQ_32000)
+ printf("32kHz ");
+ if (aptx_hd->frequency & APTX_HD_SAMPLING_FREQ_44100)
+ printf("44.1kHz ");
+ if (aptx_hd->frequency & APTX_HD_SAMPLING_FREQ_48000)
+ printf("48kHz ");
+
+ printf("\n\t\t\tChannel modes: ");
+ if (aptx_hd->channel_mode & APTX_HD_CHANNEL_MODE_MONO)
+ printf("Mono ");
+ if (aptx_hd->channel_mode & APTX_HD_CHANNEL_MODE_STEREO)
+ printf("Stereo ");
+
+ printf("\n");
+}
+
static void print_ldac(a2dp_ldac_t *ldac, uint8_t size)
{
printf("\t\tVendor Specific Value (LDAC)");
@@ -204,8 +320,27 @@ static void print_ldac(a2dp_ldac_t *ldac, uint8_t size)
return;
}
- printf("\n\t\t\tUnknown: %02x %02x", ldac->frequency,
- ldac->channel_mode);
+ printf("\n\t\t\tFrequencies: ");
+ if (ldac->frequency & LDAC_SAMPLING_FREQ_44100)
+ printf("44.1kHz ");
+ if (ldac->frequency & LDAC_SAMPLING_FREQ_48000)
+ printf("48kHz ");
+ if (ldac->frequency & LDAC_SAMPLING_FREQ_88200)
+ printf("88.2kHz ");
+ if (ldac->frequency & LDAC_SAMPLING_FREQ_96000)
+ printf("96kHz ");
+ if (ldac->frequency & LDAC_SAMPLING_FREQ_176400)
+ printf("176.4kHz ");
+ if (ldac->frequency & LDAC_SAMPLING_FREQ_192000)
+ printf("192kHz ");
+
+ printf("\n\t\t\tChannel modes: ");
+ if (ldac->channel_mode & LDAC_CHANNEL_MODE_MONO)
+ printf("Mono ");
+ if (ldac->channel_mode & LDAC_CHANNEL_MODE_DUAL)
+ printf("Dual ");
+ if (ldac->channel_mode & LDAC_CHANNEL_MODE_STEREO)
+ printf("Stereo ");
printf("\n");
}
@@ -237,6 +372,13 @@ static void print_vendor(a2dp_vendor_codec_t *vendor, uint8_t size)
if (vendor_id == APTX_VENDOR_ID && codec_id == APTX_CODEC_ID)
print_aptx((void *) vendor, size);
+ else if (vendor_id == FASTSTREAM_VENDOR_ID &&
+ codec_id == FASTSTREAM_CODEC_ID)
+ print_faststream((void *) vendor, size);
+ else if (vendor_id == APTX_LL_VENDOR_ID && codec_id == APTX_LL_CODEC_ID)
+ print_aptx_ll((void *) vendor, size);
+ else if (vendor_id == APTX_HD_VENDOR_ID && codec_id == APTX_HD_CODEC_ID)
+ print_aptx_hd((void *) vendor, size);
else if (vendor_id == LDAC_VENDOR_ID && codec_id == LDAC_CODEC_ID)
print_ldac((void *) vendor, size);
}
--
2.21.0
From 61bc87a7702f0ed544d190444938ebece135547d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sun, 23 Dec 2018 11:00:43 +0100
Subject: [PATCH 11/31] btmon: Parse new A2DP codecs
Parse information about additional A2DP codecs: FastStream, aptX Low
Latency and aptX HD.
---
monitor/a2dp.c | 296 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 284 insertions(+), 12 deletions(-)
diff --git a/monitor/a2dp.c b/monitor/a2dp.c
index 94f9758aa..3e798a7ee 100644
--- a/monitor/a2dp.c
+++ b/monitor/a2dp.c
@@ -3,6 +3,7 @@
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2015 Andrzej Kaczmarek <andrzej.kaczmarek@codecoup.pl>
+ * Copyright (C) 2018 Pali Rohár <pali.rohar@gmail.com>
*
*
* This library is free software; you can redistribute it and/or
@@ -50,6 +51,12 @@
/* Vendor Specific A2DP Codecs */
#define APTX_VENDOR_ID 0x0000004f
#define APTX_CODEC_ID 0x0001
+#define FASTSTREAM_VENDOR_ID 0x0000000a
+#define FASTSTREAM_CODEC_ID 0x0001
+#define APTX_LL_VENDOR_ID 0x0000000a
+#define APTX_LL_CODEC_ID 0x0002
+#define APTX_HD_VENDOR_ID 0x000000D7
+#define APTX_HD_CODEC_ID 0x0024
#define LDAC_VENDOR_ID 0x0000012d
#define LDAC_CODEC_ID 0x00aa
@@ -186,6 +193,23 @@ static const struct bit_desc aptx_channel_mode_table[] = {
{ }
};
+static const struct bit_desc faststream_direction_table[] = {
+ { 0, "Sink" },
+ { 1, "Source" },
+ { }
+};
+
+static const struct bit_desc faststream_sink_frequency_table[] = {
+ { 1, "44100" },
+ { 0, "48000" },
+ { }
+};
+
+static const struct bit_desc faststream_source_frequency_table[] = {
+ { 5, "16000" },
+ { }
+};
+
static void print_value_bits(uint8_t indent, uint32_t value,
const struct bit_desc *table)
{
@@ -210,12 +234,49 @@ static const char *find_value_bit(uint32_t value,
return "Unknown";
}
+struct vndcodec {
+ uint32_t vendor_id;
+ uint16_t codec_id;
+ char *codec_name;
+ bool (*codec_vendor_cap)(uint8_t losc, struct l2cap_frame *frame);
+ bool (*codec_vendor_cfg)(uint8_t losc, struct l2cap_frame *frame);
+};
+
+static bool codec_vendor_aptx_cap(uint8_t losc, struct l2cap_frame *frame);
+static bool codec_vendor_aptx_cfg(uint8_t losc, struct l2cap_frame *frame);
+static bool codec_vendor_faststream_cap(uint8_t losc,
+ struct l2cap_frame *frame);
+static bool codec_vendor_faststream_cfg(uint8_t losc,
+ struct l2cap_frame *frame);
+static bool codec_vendor_aptx_ll_cap(uint8_t losc, struct l2cap_frame *frame);
+static bool codec_vendor_aptx_ll_cfg(uint8_t losc, struct l2cap_frame *frame);
+static bool codec_vendor_aptx_hd_cap(uint8_t losc, struct l2cap_frame *frame);
+static bool codec_vendor_aptx_hd_cfg(uint8_t losc, struct l2cap_frame *frame);
+static bool codec_vendor_ldac(uint8_t losc, struct l2cap_frame *frame);
+
+static const struct vndcodec vndcodecs[] = {
+ { APTX_VENDOR_ID, APTX_CODEC_ID, "aptX",
+ codec_vendor_aptx_cap, codec_vendor_aptx_cfg },
+ { FASTSTREAM_VENDOR_ID, FASTSTREAM_CODEC_ID, "FastStream",
+ codec_vendor_faststream_cap, codec_vendor_faststream_cfg },
+ { APTX_LL_VENDOR_ID, APTX_LL_CODEC_ID, "aptX Low Latency",
+ codec_vendor_aptx_ll_cap, codec_vendor_aptx_ll_cfg },
+ { APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID, "aptX HD",
+ codec_vendor_aptx_hd_cap, codec_vendor_aptx_hd_cfg },
+ { LDAC_VENDOR_ID, LDAC_CODEC_ID, "LDAC",
+ codec_vendor_ldac, codec_vendor_ldac },
+ { }
+};
+
static const char *vndcodec2str(uint32_t vendor_id, uint16_t codec_id)
{
- if (vendor_id == APTX_VENDOR_ID && codec_id == APTX_CODEC_ID)
- return "aptX";
- else if (vendor_id == LDAC_VENDOR_ID && codec_id == LDAC_CODEC_ID)
- return "LDAC";
+ size_t i;
+
+ for (i = 0; i < sizeof(vndcodecs)/sizeof(*vndcodecs); i++) {
+ if (vndcodecs[i].vendor_id == vendor_id &&
+ vndcodecs[i].codec_id == codec_id)
+ return vndcodecs[i].codec_name;
+ }
return "Unknown";
}
@@ -507,6 +568,108 @@ static bool codec_vendor_aptx_cap(uint8_t losc, struct l2cap_frame *frame)
return true;
}
+static bool codec_vendor_faststream_cap(uint8_t losc, struct l2cap_frame *frame)
+{
+ uint8_t cap = 0;
+
+ if (losc != 2)
+ return false;
+
+ l2cap_frame_get_u8(frame, &cap);
+
+ print_field("%*cDirection: 0x%02x", BASE_INDENT + 2, ' ', cap);
+ print_value_bits(BASE_INDENT + 2, cap, faststream_direction_table);
+
+ l2cap_frame_get_u8(frame, &cap);
+
+ print_field("%*cSink Frequency: 0x%02x", BASE_INDENT + 2, ' ',
+ cap & 0x0f);
+ print_value_bits(BASE_INDENT + 2, cap & 0x0f,
+ faststream_sink_frequency_table);
+
+ print_field("%*cSource Frequency: 0x%02x", BASE_INDENT + 2, ' ',
+ cap & 0xf0);
+ print_value_bits(BASE_INDENT + 2, cap & 0xf0,
+ faststream_source_frequency_table);
+
+ return true;
+}
+
+static bool codec_vendor_aptx_ll_cap(uint8_t losc, struct l2cap_frame *frame)
+{
+ uint8_t cap = 0;
+ uint16_t level = 0;
+
+ if (losc != 2 && losc != 11)
+ return false;
+
+ l2cap_frame_get_u8(frame, &cap);
+
+ print_field("%*cFrequency: 0x%02x", BASE_INDENT + 2, ' ', cap & 0xf0);
+ print_value_bits(BASE_INDENT + 2, cap & 0xf0, aptx_frequency_table);
+
+ print_field("%*cChannel Mode: 0x%02x", BASE_INDENT + 2, ' ',
+ cap & 0x0f);
+ print_value_bits(BASE_INDENT + 2, cap & 0x0f, aptx_channel_mode_table);
+
+ l2cap_frame_get_u8(frame, &cap);
+
+ print_field("%*cBidirectional link: %s", BASE_INDENT, ' ',
+ (cap & 1) ? "Yes" : "No");
+
+ if ((cap & 2) && losc == 11) {
+ /* reserved */
+ l2cap_frame_get_u8(frame, &cap);
+
+ l2cap_frame_get_le16(frame, &level);
+ print_field("%*cTarget codec buffer level: %u (0x%02x)",
+ BASE_INDENT + 2, ' ', level, level);
+
+ l2cap_frame_get_le16(frame, &level);
+ print_field("%*cInitial codec buffer level: %u (0x%02x)",
+ BASE_INDENT + 2, ' ', level, level);
+
+ l2cap_frame_get_u8(frame, &cap);
+ print_field("%*cSRA max rate: %g (0x%02x)",
+ BASE_INDENT + 2, ' ', cap / 10000.0, cap);
+
+ l2cap_frame_get_u8(frame, &cap);
+ print_field("%*cSRA averaging time: %us (0x%02x)",
+ BASE_INDENT + 2, ' ', cap, cap);
+
+ l2cap_frame_get_le16(frame, &level);
+ print_field("%*cGood working codec buffer level: %u (0x%02x)",
+ BASE_INDENT + 2, ' ', level, level);
+ }
+
+ return true;
+}
+
+static bool codec_vendor_aptx_hd_cap(uint8_t losc, struct l2cap_frame *frame)
+{
+ uint8_t cap = 0;
+
+ if (losc != 5)
+ return false;
+
+ l2cap_frame_get_u8(frame, &cap);
+
+ print_field("%*cFrequency: 0x%02x", BASE_INDENT + 2, ' ', cap & 0xf0);
+ print_value_bits(BASE_INDENT + 2, cap & 0xf0, aptx_frequency_table);
+
+ print_field("%*cChannel Mode: 0x%02x", BASE_INDENT + 2, ' ',
+ cap & 0x0f);
+ print_value_bits(BASE_INDENT + 2, cap & 0x0f, aptx_channel_mode_table);
+
+ /* reserved */
+ l2cap_frame_get_u8(frame, &cap);
+ l2cap_frame_get_u8(frame, &cap);
+ l2cap_frame_get_u8(frame, &cap);
+ l2cap_frame_get_u8(frame, &cap);
+
+ return true;
+}
+
static bool codec_vendor_ldac(uint8_t losc, struct l2cap_frame *frame)
{
uint16_t cap = 0;
@@ -525,6 +688,7 @@ static bool codec_vendor_cap(uint8_t losc, struct l2cap_frame *frame)
{
uint32_t vendor_id = 0;
uint16_t codec_id = 0;
+ size_t i;
if (losc < 6)
return false;
@@ -540,10 +704,11 @@ static bool codec_vendor_cap(uint8_t losc, struct l2cap_frame *frame)
print_field("%*cVendor Specific Codec ID: %s (0x%04x)", BASE_INDENT,
' ', vndcodec2str(vendor_id, codec_id), codec_id);
- if (vendor_id == APTX_VENDOR_ID && codec_id == APTX_CODEC_ID)
- return codec_vendor_aptx_cap(losc, frame);
- else if (vendor_id == LDAC_VENDOR_ID && codec_id == LDAC_CODEC_ID)
- return codec_vendor_ldac(losc, frame);
+ for (i = 0; i < sizeof(vndcodecs)/sizeof(*vndcodecs); i++) {
+ if (vndcodecs[i].vendor_id == vendor_id &&
+ vndcodecs[i].codec_id == codec_id)
+ return vndcodecs[i].codec_vendor_cap(losc, frame);
+ }
packet_hexdump(frame->data, losc);
l2cap_frame_pull(frame, frame, losc);
@@ -571,10 +736,116 @@ static bool codec_vendor_aptx_cfg(uint8_t losc, struct l2cap_frame *frame)
return true;
}
+static bool codec_vendor_faststream_cfg(uint8_t losc, struct l2cap_frame *frame)
+{
+ uint8_t cap = 0;
+
+ if (losc != 2)
+ return false;
+
+ l2cap_frame_get_u8(frame, &cap);
+
+ print_field("%*cDirection: %s (0x%02x)", BASE_INDENT + 2, ' ',
+ find_value_bit(cap, faststream_direction_table),
+ cap);
+
+ l2cap_frame_get_u8(frame, &cap);
+
+ print_field("%*cSink Frequency: %s (0x%02x)", BASE_INDENT + 2, ' ',
+ find_value_bit(cap & 0x0f,
+ faststream_sink_frequency_table),
+ cap & 0x0f);
+
+ print_field("%*cSource Frequency: %s (0x%02x)", BASE_INDENT + 2, ' ',
+ find_value_bit(cap & 0xf0,
+ faststream_source_frequency_table),
+ cap & 0xf0);
+
+ return true;
+}
+
+static bool codec_vendor_aptx_ll_cfg(uint8_t losc, struct l2cap_frame *frame)
+{
+ uint8_t cap = 0;
+ uint16_t level = 0;
+
+ if (losc != 2 && losc != 11)
+ return false;
+
+ l2cap_frame_get_u8(frame, &cap);
+
+ print_field("%*cFrequency: %s (0x%02x)", BASE_INDENT + 2, ' ',
+ find_value_bit(cap & 0xf0, aptx_frequency_table),
+ cap & 0xf0);
+
+ print_field("%*cChannel Mode: %s (0x%02x)", BASE_INDENT + 2, ' ',
+ find_value_bit(cap & 0x0f, aptx_channel_mode_table),
+ cap & 0x0f);
+
+ l2cap_frame_get_u8(frame, &cap);
+
+ print_field("%*cBidirectional link: %s", BASE_INDENT, ' ',
+ (cap & 1) ? "Yes" : "No");
+
+ if ((cap & 2) && losc == 11) {
+ /* reserved */
+ l2cap_frame_get_u8(frame, &cap);
+
+ l2cap_frame_get_le16(frame, &level);
+ print_field("%*cTarget codec buffer level: %u (0x%02x)",
+ BASE_INDENT + 2, ' ', level, level);
+
+ l2cap_frame_get_le16(frame, &level);
+ print_field("%*cInitial codec buffer level: %u (0x%02x)",
+ BASE_INDENT + 2, ' ', level, level);
+
+ l2cap_frame_get_u8(frame, &cap);
+ print_field("%*cSRA max rate: %g (0x%02x)",
+ BASE_INDENT + 2, ' ', cap / 10000.0, cap);
+
+ l2cap_frame_get_u8(frame, &cap);
+ print_field("%*cSRA averaging time: %us (0x%02x)",
+ BASE_INDENT + 2, ' ', cap, cap);
+
+ l2cap_frame_get_le16(frame, &level);
+ print_field("%*cGood working codec buffer level: %u (0x%02x)",
+ BASE_INDENT + 2, ' ', level, level);
+ }
+
+ return true;
+}
+
+static bool codec_vendor_aptx_hd_cfg(uint8_t losc, struct l2cap_frame *frame)
+{
+ uint8_t cap = 0;
+
+ if (losc != 5)
+ return false;
+
+ l2cap_frame_get_u8(frame, &cap);
+
+ print_field("%*cFrequency: %s (0x%02x)", BASE_INDENT + 2, ' ',
+ find_value_bit(cap & 0xf0, aptx_frequency_table),
+ cap & 0xf0);
+
+ print_field("%*cChannel Mode: %s (0x%02x)", BASE_INDENT + 2, ' ',
+ find_value_bit(cap & 0x0f, aptx_channel_mode_table),
+ cap & 0x0f);
+
+ /* reserved */
+ l2cap_frame_get_u8(frame, &cap);
+ l2cap_frame_get_u8(frame, &cap);
+ l2cap_frame_get_u8(frame, &cap);
+ l2cap_frame_get_u8(frame, &cap);
+
+ return true;
+}
+
static bool codec_vendor_cfg(uint8_t losc, struct l2cap_frame *frame)
{
uint32_t vendor_id = 0;
uint16_t codec_id = 0;
+ size_t i;
if (losc < 6)
return false;
@@ -590,10 +861,11 @@ static bool codec_vendor_cfg(uint8_t losc, struct l2cap_frame *frame)
print_field("%*cVendor Specific Codec ID: %s (0x%04x)", BASE_INDENT,
' ', vndcodec2str(vendor_id, codec_id), codec_id);
- if (vendor_id == APTX_VENDOR_ID && codec_id == APTX_CODEC_ID)
- return codec_vendor_aptx_cfg(losc, frame);
- else if (vendor_id == LDAC_VENDOR_ID && codec_id == LDAC_CODEC_ID)
- return codec_vendor_ldac(losc, frame);
+ for (i = 0; i < sizeof(vndcodecs)/sizeof(*vndcodecs); i++) {
+ if (vndcodecs[i].vendor_id == vendor_id &&
+ vndcodecs[i].codec_id == codec_id)
+ return vndcodecs[i].codec_vendor_cfg(losc, frame);
+ }
packet_hexdump(frame->data, losc);
l2cap_frame_pull(frame, frame, losc);
--
2.21.0
From fcad924159d772586fe9e402985fee64ae29b70a Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Thu, 3 Jan 2019 13:45:43 -0300
Subject: [PATCH 12/31] a2dp: Expose remote SEP
This implements MediaEndpoint for remote SEP which can be used by
clients to switch configuration on demand.
---
profiles/audio/a2dp.c | 351 ++++++++++++++++++++++++++++++++++++++++-
profiles/audio/a2dp.h | 1 +
profiles/audio/avdtp.c | 10 ++
profiles/audio/avdtp.h | 4 +
profiles/audio/media.c | 8 +
5 files changed, 367 insertions(+), 7 deletions(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 344459332..4025776aa 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -27,7 +27,10 @@
#include <config.h>
#endif
+#define _GNU_SOURCE
+
#include <stdlib.h>
+#include <stdio.h>
#include <errno.h>
#include <dbus/dbus.h>
@@ -38,14 +41,19 @@
#include "lib/sdp_lib.h"
#include "lib/uuid.h"
+#include "gdbus/gdbus.h"
+
#include "src/plugin.h"
#include "src/adapter.h"
#include "src/device.h"
+#include "src/dbus-common.h"
+#include "src/error.h"
#include "src/profile.h"
#include "src/service.h"
#include "src/log.h"
#include "src/sdpd.h"
#include "src/shared/queue.h"
+#include "src/shared/util.h"
#include "btio/btio.h"
@@ -63,6 +71,8 @@
#define AVDTP_PSM 25
+#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
+
struct a2dp_sep {
struct a2dp_server *server;
struct a2dp_endpoint *endpoint;
@@ -93,6 +103,7 @@ struct a2dp_setup_cb {
};
struct a2dp_setup {
+ struct a2dp_channel *chan;
struct avdtp *session;
struct a2dp_sep *sep;
struct avdtp_remote_sep *rsep;
@@ -121,6 +132,12 @@ struct a2dp_server {
struct queue *channels;
};
+struct a2dp_remote_sep {
+ struct a2dp_channel *chan;
+ char *path;
+ struct avdtp_remote_sep *sep;
+};
+
struct a2dp_channel {
struct a2dp_server *server;
struct btd_device *device;
@@ -129,6 +146,7 @@ struct a2dp_channel {
unsigned int state_id;
unsigned int auth_id;
struct avdtp *session;
+ struct queue *seps;
};
static GSList *servers = NULL;
@@ -144,12 +162,42 @@ static struct a2dp_setup *setup_ref(struct a2dp_setup *setup)
return setup;
}
+static bool match_by_session(const void *data, const void *user_data)
+{
+ const struct a2dp_channel *chan = data;
+ const struct avdtp *session = user_data;
+
+ return chan->session == session;
+}
+
+static struct a2dp_channel *find_channel(struct avdtp *session)
+{
+ GSList *l;
+
+ for (l = servers; l; l = g_slist_next(l)) {
+ struct a2dp_server *server = l->data;
+ struct a2dp_channel *chan;
+
+ chan = queue_find(server->channels, match_by_session, session);
+ if (chan)
+ return chan;
+ }
+
+ return NULL;
+}
+
static struct a2dp_setup *setup_new(struct avdtp *session)
{
struct a2dp_setup *setup;
+ struct a2dp_channel *chan;
+
+ chan = find_channel(session);
+ if (!chan)
+ return NULL;
setup = g_new0(struct a2dp_setup, 1);
setup->session = avdtp_ref(session);
+ setup->chan = find_channel(session);
setups = g_slist_append(setups, setup);
return setup;
@@ -1299,6 +1347,14 @@ static struct a2dp_server *find_server(GSList *list, struct btd_adapter *a)
return NULL;
}
+static void remove_remote_sep(void *data)
+{
+ struct a2dp_remote_sep *sep = data;
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(), sep->path,
+ MEDIA_ENDPOINT_INTERFACE);
+}
+
static void channel_free(void *data)
{
struct a2dp_channel *chan = data;
@@ -1316,6 +1372,7 @@ static void channel_free(void *data)
avdtp_remove_state_cb(chan->state_id);
+ queue_destroy(chan->seps, remove_remote_sep);
g_free(chan);
}
@@ -1371,6 +1428,7 @@ static struct a2dp_channel *channel_new(struct a2dp_server *server,
chan = g_new0(struct a2dp_channel, 1);
chan->server = server;
chan->device = device;
+ chan->seps = queue_new();
chan->state_id = avdtp_add_state_cb(device, avdtp_state_cb, chan);
if (!queue_push_tail(server->channels, chan)) {
@@ -1805,16 +1863,11 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
a2dp_unregister_sep(sep);
}
-static void select_cb(struct a2dp_setup *setup, void *ret, int size)
+static void setup_add_caps(struct a2dp_setup *setup, uint8_t *caps, size_t size)
{
struct avdtp_service_capability *media_transport, *media_codec;
struct avdtp_media_codec_capability *cap;
- if (size < 0) {
- DBG("Endpoint replied an invalid configuration");
- goto done;
- }
-
media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
NULL, 0);
@@ -1823,13 +1876,23 @@ static void select_cb(struct a2dp_setup *setup, void *ret, int size)
cap = g_malloc0(sizeof(*cap) + size);
cap->media_type = AVDTP_MEDIA_TYPE_AUDIO;
cap->media_codec_type = setup->sep->codec;
- memcpy(cap->data, ret, size);
+ memcpy(cap->data, caps, size);
media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap,
sizeof(*cap) + size);
setup->caps = g_slist_append(setup->caps, media_codec);
g_free(cap);
+}
+
+static void select_cb(struct a2dp_setup *setup, void *ret, int size)
+{
+ if (size < 0) {
+ DBG("Endpoint replied an invalid configuration");
+ goto done;
+ }
+
+ setup_add_caps(setup, ret, size);
done:
finalize_select(setup);
@@ -1885,6 +1948,277 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
return a2dp_find_sep(session, l, NULL);
}
+struct client {
+ const char *sender;
+ const char *path;
+};
+
+static int match_client(const void *data, const void *user_data)
+{
+ struct a2dp_sep *sep = (void *) data;
+ const struct a2dp_endpoint *endpoint = sep->endpoint;
+ const struct client *client = user_data;
+
+ if (strcmp(client->sender, endpoint->get_name(sep, sep->user_data)))
+ return -1;
+
+ return strcmp(client->path, endpoint->get_path(sep, sep->user_data));
+}
+
+static struct a2dp_sep *find_sep(struct a2dp_server *server, const char *sender,
+ const char *path)
+{
+ GSList *l;
+ struct client client = { sender, path };
+
+ l = g_slist_find_custom(server->sources, &client, match_client);
+ if (l)
+ return l->data;
+
+ l = g_slist_find_custom(server->sinks, &client, match_client);
+ if (l)
+ return l->data;
+
+ return NULL;
+}
+
+static int parse_properties(DBusMessageIter *props, uint8_t **caps, int *size)
+{
+ while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(props, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+ if (strcasecmp(key, "Capabilities") == 0) {
+ DBusMessageIter array;
+
+ if (var != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&value, &array);
+ dbus_message_iter_get_fixed_array(&array, caps, size);
+ return 0;
+ }
+
+ dbus_message_iter_next(props);
+ }
+
+ return -EINVAL;
+}
+
+static int a2dp_reconfig(struct a2dp_channel *chan, const char *sender,
+ struct a2dp_sep *lsep, struct a2dp_remote_sep *rsep,
+ uint8_t *caps, int size)
+{
+ struct a2dp_setup *setup;
+ const struct queue_entry *entry;
+ int err;
+
+ setup = a2dp_setup_get(chan->session);
+ if (!setup)
+ return -ENOMEM;
+
+ setup->sep = lsep;
+ setup->rsep = rsep->sep;
+
+ setup_add_caps(setup, caps, size);
+
+ /* Check for existing stream and close it */
+ for (entry = queue_get_entries(chan->server->seps); entry;
+ entry = entry->next) {
+ struct a2dp_sep *tmp = entry->data;
+
+ /* Attempt to reconfigure if a stream already exists */
+ if (tmp->stream) {
+ /* Only allow switching sep from the same sender */
+ if (strcmp(sender, tmp->endpoint->get_name(tmp,
+ tmp->user_data)))
+ return -EPERM;
+
+ err = avdtp_close(chan->session, tmp->stream, FALSE);
+ if (err < 0) {
+ error("avdtp_close: %s", strerror(-err));
+ return err;
+ }
+
+ setup->reconfigure = TRUE;
+
+ return 0;
+ }
+ }
+
+ err = avdtp_set_configuration(setup->session, setup->rsep,
+ lsep->lsep,
+ setup->caps,
+ &setup->stream);
+ if (err < 0) {
+ error("avdtp_set_configuration: %s", strerror(-err));
+ return err;
+ }
+
+ return 0;
+}
+
+static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct a2dp_remote_sep *rsep = data;
+ struct a2dp_channel *chan = rsep->chan;
+ struct a2dp_sep *lsep;
+ struct avdtp_service_capability *service;
+ struct avdtp_media_codec_capability *codec;
+ DBusMessageIter args, props;
+ const char *sender, *path;
+ uint8_t *caps;
+ int err, size = 0;
+
+ sender = dbus_message_get_sender(msg);
+
+ dbus_message_iter_init(msg, &args);
+
+ dbus_message_iter_get_basic(&args, &path);
+ dbus_message_iter_next(&args);
+
+ lsep = find_sep(chan->server, sender, path);
+ if (!lsep)
+ return btd_error_invalid_args(msg);
+
+ /* Check if SEPs are no the same role */
+ if (avdtp_get_type(rsep->sep) == lsep->type)
+ return btd_error_invalid_args(msg);
+
+ service = avdtp_get_codec(rsep->sep);
+ codec = (struct avdtp_media_codec_capability *) service->data;
+
+ /* Check if codec match */
+ if (!endpoint_match_codec_ind(chan->session, codec, lsep))
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&args, &props);
+ if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+ return btd_error_invalid_args(msg);
+
+ if (parse_properties(&props, &caps, &size) < 0)
+ return btd_error_invalid_args(msg);
+
+ err = a2dp_reconfig(chan, sender, lsep, rsep, caps, size);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable sep_methods[] = {
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("SetConfiguration",
+ GDBUS_ARGS({ "endpoint", "o" },
+ { "properties", "a{sv}" } ),
+ NULL, set_configuration) },
+ { },
+};
+
+static gboolean get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct a2dp_remote_sep *sep = data;
+ const char *uuid;
+
+ switch (avdtp_get_type(sep->sep)) {
+ case AVDTP_SEP_TYPE_SOURCE:
+ uuid = A2DP_SOURCE_UUID;
+ break;
+ case AVDTP_SEP_TYPE_SINK:
+ uuid = A2DP_SOURCE_UUID;
+ break;
+ default:
+ uuid = "";
+ }
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+ return TRUE;
+}
+
+static gboolean get_codec(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct a2dp_remote_sep *sep = data;
+ struct avdtp_service_capability *cap = avdtp_get_codec(sep->sep);
+ struct avdtp_media_codec_capability *codec = (void *) cap->data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &codec->media_codec_type);
+
+ return TRUE;
+}
+
+static gboolean get_capabilities(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct a2dp_remote_sep *sep = data;
+ struct avdtp_service_capability *service = avdtp_get_codec(sep->sep);
+ struct avdtp_media_codec_capability *codec = (void *) service->data;
+ uint8_t *caps = codec->data;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &caps,
+ service->length - sizeof(*codec));
+
+ dbus_message_iter_close_container(iter, &array);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable sep_properties[] = {
+ { "UUID", "s", get_uuid, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Codec", "y", get_codec, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Capabilities", "ay", get_capabilities, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { }
+};
+
+static void remote_sep_free(void *data)
+{
+ struct a2dp_remote_sep *sep = data;
+
+ free(sep->path);
+ free(sep);
+}
+
+static void register_remote_sep(void *data, void *user_data)
+{
+ struct avdtp_remote_sep *rsep = data;
+ struct a2dp_setup *setup = user_data;
+ struct a2dp_remote_sep *sep;
+
+ sep = new0(struct a2dp_remote_sep, 1);
+ sep->chan = setup->chan;
+ sep->sep = rsep;
+ asprintf(&sep->path, "%s/sep%d", device_get_path(setup->chan->device),
+ avdtp_get_seid(rsep));
+
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ sep->path, MEDIA_ENDPOINT_INTERFACE,
+ sep_methods, NULL, sep_properties,
+ sep, remote_sep_free) == FALSE) {
+ error("Could not register remote sep %s", sep->path);
+ remote_sep_free(sep);
+ }
+
+ queue_push_tail(setup->chan->seps, sep);
+}
+
static void discover_cb(struct avdtp *session, GSList *seps,
struct avdtp_error *err, void *user_data)
{
@@ -1895,6 +2229,9 @@ static void discover_cb(struct avdtp *session, GSList *seps,
setup->seps = seps;
setup->err = err;
+ if (!err && queue_isempty(setup->chan->seps))
+ g_slist_foreach(seps, register_remote_sep, setup);
+
finalize_discover(setup);
}
diff --git a/profiles/audio/a2dp.h b/profiles/audio/a2dp.h
index 2c388bb68..7f38c75f3 100644
--- a/profiles/audio/a2dp.h
+++ b/profiles/audio/a2dp.h
@@ -32,6 +32,7 @@ typedef void (*a2dp_endpoint_config_t) (struct a2dp_setup *setup, gboolean ret);
struct a2dp_endpoint {
const char *(*get_name) (struct a2dp_sep *sep, void *user_data);
+ const char *(*get_path) (struct a2dp_sep *sep, void *user_data);
size_t (*get_capabilities) (struct a2dp_sep *sep,
uint8_t **capabilities,
void *user_data);
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 2cb3c8a00..cc4322d10 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -3161,6 +3161,16 @@ static int process_queue(struct avdtp *session)
return send_req(session, FALSE, req);
}
+uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep)
+{
+ return sep->seid;
+}
+
+uint8_t avdtp_get_type(struct avdtp_remote_sep *sep)
+{
+ return sep->type;
+}
+
struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep)
{
return sep->codec;
diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
index 621a6e3cf..e5fc40c89 100644
--- a/profiles/audio/avdtp.h
+++ b/profiles/audio/avdtp.h
@@ -223,6 +223,10 @@ struct avdtp *avdtp_ref(struct avdtp *session);
struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
void *data, int size);
+uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep);
+
+uint8_t avdtp_get_type(struct avdtp_remote_sep *sep);
+
struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep);
int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 23d15611b..9b833b6aa 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -488,6 +488,13 @@ static const char *get_name(struct a2dp_sep *sep, void *user_data)
return endpoint->sender;
}
+static const char *get_path(struct a2dp_sep *sep, void *user_data)
+{
+ struct media_endpoint *endpoint = user_data;
+
+ return endpoint->path;
+}
+
static size_t get_capabilities(struct a2dp_sep *sep, uint8_t **capabilities,
void *user_data)
{
@@ -578,6 +585,7 @@ static void set_delay(struct a2dp_sep *sep, uint16_t delay, void *user_data)
static struct a2dp_endpoint a2dp_endpoint = {
.get_name = get_name,
+ .get_path = get_path,
.get_capabilities = get_capabilities,
.select_configuration = select_config,
.set_configuration = set_config,
--
2.21.0
From dc9b175cc89621a2abbae5b661bfc61c68191abe Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Fri, 4 Jan 2019 16:22:05 -0300
Subject: [PATCH 13/31] doc/media-api: Add Endpoint property to MediaTransport
Adds endpoint object to MediaTransport so application can resolve which
MediaEndpoint is in use.
---
doc/media-api.txt | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/doc/media-api.txt b/doc/media-api.txt
index b5ad2db12..93c3490b6 100644
--- a/doc/media-api.txt
+++ b/doc/media-api.txt
@@ -604,3 +604,8 @@ Properties object Device [readonly]
acquired by the sender.
Possible Values: 0-127
+
+ object Endpoint [readonly, optional, experimental]
+
+ Endpoint object which the transport is associated
+ with.
--
2.21.0
From e7795ccbc1810a7a398fb059638f289644447ab1 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Fri, 4 Jan 2019 16:24:18 -0300
Subject: [PATCH 14/31] a2dp: Implement MediaTransport.Endpoint
This implements MediaTransport.Endpoint property which exposes what
endpoint is being used by the transport.
---
profiles/audio/a2dp.c | 91 +++++++++++++++++++++++++++++---------
profiles/audio/a2dp.h | 1 +
profiles/audio/media.c | 5 ++-
profiles/audio/transport.c | 28 +++++++++++-
profiles/audio/transport.h | 1 +
5 files changed, 102 insertions(+), 24 deletions(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 4025776aa..4fa01894a 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -106,7 +106,7 @@ struct a2dp_setup {
struct a2dp_channel *chan;
struct avdtp *session;
struct a2dp_sep *sep;
- struct avdtp_remote_sep *rsep;
+ struct a2dp_remote_sep *rsep;
struct avdtp_stream *stream;
struct avdtp_error *err;
avdtp_set_configuration_cb setconf_cb;
@@ -1065,6 +1065,24 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
return TRUE;
}
+static bool match_remote_sep(const void *data, const void *user_data)
+{
+ const struct a2dp_remote_sep *sep = data;
+ const struct avdtp_remote_sep *rsep = user_data;
+
+ return sep->sep == rsep;
+}
+
+static struct a2dp_remote_sep *find_remote_sep(struct a2dp_channel *chan,
+ struct a2dp_sep *sep)
+{
+ struct avdtp_remote_sep *rsep;
+
+ rsep = avdtp_find_remote_sep(chan->session, sep->lsep);
+
+ return queue_find(chan->seps, match_remote_sep, rsep);
+}
+
static gboolean a2dp_reconfigure(gpointer data)
{
struct a2dp_setup *setup = data;
@@ -1074,14 +1092,14 @@ static gboolean a2dp_reconfigure(gpointer data)
struct avdtp_service_capability *cap;
if (setup->rsep) {
- cap = avdtp_get_codec(setup->rsep);
+ cap = avdtp_get_codec(setup->rsep->sep);
rsep_codec = (struct avdtp_media_codec_capability *) cap->data;
}
if (!setup->rsep || sep->codec != rsep_codec->media_codec_type)
- setup->rsep = avdtp_find_remote_sep(setup->session, sep->lsep);
+ setup->rsep = find_remote_sep(setup->chan, sep);
- posix_err = avdtp_set_configuration(setup->session, setup->rsep,
+ posix_err = avdtp_set_configuration(setup->session, setup->rsep->sep,
sep->lsep,
setup->caps,
&setup->stream);
@@ -1097,6 +1115,16 @@ failed:
return FALSE;
}
+static struct a2dp_remote_sep *get_remote_sep(struct a2dp_channel *chan,
+ struct avdtp_stream *stream)
+{
+ struct avdtp_remote_sep *rsep;
+
+ rsep = avdtp_stream_get_remote_sep(stream);
+
+ return queue_find(chan->seps, match_remote_sep, rsep);
+}
+
static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, struct avdtp_error *err,
void *user_data)
@@ -1121,7 +1149,7 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
}
if (!setup->rsep)
- setup->rsep = avdtp_stream_get_remote_sep(stream);
+ setup->rsep = get_remote_sep(setup->chan, stream);
if (setup->reconfigure)
g_timeout_add(RECONFIGURE_TIMEOUT, a2dp_reconfigure, setup);
@@ -1347,10 +1375,23 @@ static struct a2dp_server *find_server(GSList *list, struct btd_adapter *a)
return NULL;
}
+static void remote_sep_free(void *data)
+{
+ struct a2dp_remote_sep *sep = data;
+
+ free(sep->path);
+ free(sep);
+}
+
static void remove_remote_sep(void *data)
{
struct a2dp_remote_sep *sep = data;
+ if (!sep->path) {
+ remote_sep_free(sep);
+ return;
+ }
+
g_dbus_unregister_interface(btd_get_dbus_connection(), sep->path,
MEDIA_ENDPOINT_INTERFACE);
}
@@ -2026,7 +2067,7 @@ static int a2dp_reconfig(struct a2dp_channel *chan, const char *sender,
return -ENOMEM;
setup->sep = lsep;
- setup->rsep = rsep->sep;
+ setup->rsep = rsep;
setup_add_caps(setup, caps, size);
@@ -2054,7 +2095,7 @@ static int a2dp_reconfig(struct a2dp_channel *chan, const char *sender,
}
}
- err = avdtp_set_configuration(setup->session, setup->rsep,
+ err = avdtp_set_configuration(setup->session, setup->rsep->sep,
lsep->lsep,
setup->caps,
&setup->stream);
@@ -2188,14 +2229,6 @@ static const GDBusPropertyTable sep_properties[] = {
{ }
};
-static void remote_sep_free(void *data)
-{
- struct a2dp_remote_sep *sep = data;
-
- free(sep->path);
- free(sep);
-}
-
static void register_remote_sep(void *data, void *user_data)
{
struct avdtp_remote_sep *rsep = data;
@@ -2205,6 +2238,10 @@ static void register_remote_sep(void *data, void *user_data)
sep = new0(struct a2dp_remote_sep, 1);
sep->chan = setup->chan;
sep->sep = rsep;
+
+ if (!(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL))
+ goto done;
+
asprintf(&sep->path, "%s/sep%d", device_get_path(setup->chan->device),
avdtp_get_seid(rsep));
@@ -2213,9 +2250,13 @@ static void register_remote_sep(void *data, void *user_data)
sep_methods, NULL, sep_properties,
sep, remote_sep_free) == FALSE) {
error("Could not register remote sep %s", sep->path);
- remote_sep_free(sep);
+ free(sep->path);
+ sep->path = NULL;
}
+ DBG("Found remote SEP: %s", sep->path);
+
+done:
queue_push_tail(setup->chan->seps, sep);
}
@@ -2283,14 +2324,14 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
cb_data->user_data = user_data;
setup->sep = sep;
- setup->rsep = avdtp_find_remote_sep(session, sep->lsep);
+ setup->rsep = find_remote_sep(setup->chan, sep);
if (setup->rsep == NULL) {
error("Could not find remote sep");
goto fail;
}
- service = avdtp_get_codec(setup->rsep);
+ service = avdtp_get_codec(setup->rsep->sep);
codec = (struct avdtp_media_codec_capability *) service->data;
err = sep->endpoint->select_configuration(sep, codec->data,
@@ -2384,13 +2425,13 @@ unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
break;
}
- setup->rsep = avdtp_find_remote_sep(session, sep->lsep);
+ setup->rsep = find_remote_sep(setup->chan, sep);
if (setup->rsep == NULL) {
error("No matching ACP and INT SEPs found");
goto failed;
}
- posix_err = avdtp_set_configuration(session, setup->rsep,
+ posix_err = avdtp_set_configuration(session, setup->rsep->sep,
sep->lsep, caps,
&setup->stream);
if (posix_err < 0) {
@@ -2632,6 +2673,16 @@ struct btd_device *a2dp_setup_get_device(struct a2dp_setup *setup)
return avdtp_get_device(setup->session);
}
+const char *a2dp_setup_remote_path(struct a2dp_setup *setup)
+{
+ if (setup->rsep) {
+ if (setup->rsep->path)
+ return setup->rsep->path;
+ }
+
+ return NULL;
+}
+
static int a2dp_source_probe(struct btd_service *service)
{
struct btd_device *dev = btd_service_get_device(service);
diff --git a/profiles/audio/a2dp.h b/profiles/audio/a2dp.h
index 7f38c75f3..19466a428 100644
--- a/profiles/audio/a2dp.h
+++ b/profiles/audio/a2dp.h
@@ -91,4 +91,5 @@ gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session);
gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session);
struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep);
struct btd_device *a2dp_setup_get_device(struct a2dp_setup *setup);
+const char *a2dp_setup_remote_path(struct a2dp_setup *setup);
struct avdtp *a2dp_avdtp_get(struct btd_device *device);
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 9b833b6aa..e651275b4 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -429,8 +429,9 @@ static gboolean set_configuration(struct media_endpoint *endpoint,
if (transport != NULL)
return FALSE;
- transport = media_transport_create(device, configuration, size,
- endpoint);
+ transport = media_transport_create(device,
+ a2dp_setup_remote_path(data->setup),
+ configuration, size, endpoint);
if (transport == NULL)
return FALSE;
diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index b9d357ec2..7eb115fc6 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -91,6 +91,7 @@ struct a2dp_transport {
struct media_transport {
char *path; /* Transport object path */
struct btd_device *device; /* Transport device */
+ const char *remote_endpoint; /* Transport remote SEP */
struct media_endpoint *endpoint; /* Transport endpoint */
struct media_owner *owner; /* Transport owner */
uint8_t *configuration; /* Transport configuration */
@@ -688,6 +689,24 @@ static void set_volume(const GDBusPropertyTable *property,
avrcp_set_volume(transport->device, volume, notify);
}
+static gboolean endpoint_exists(const GDBusPropertyTable *property, void *data)
+{
+ struct media_transport *transport = data;
+
+ return transport->remote_endpoint != NULL;
+}
+
+static gboolean get_endpoint(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_transport *transport = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+ &transport->remote_endpoint);
+
+ return TRUE;
+}
+
static const GDBusMethodTable transport_methods[] = {
{ GDBUS_ASYNC_METHOD("Acquire",
NULL,
@@ -711,6 +730,8 @@ static const GDBusPropertyTable transport_properties[] = {
{ "State", "s", get_state },
{ "Delay", "q", get_delay, NULL, delay_exists },
{ "Volume", "q", get_volume, set_volume, volume_exists },
+ { "Endpoint", "o", get_endpoint, NULL, endpoint_exists,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ }
};
@@ -835,6 +856,7 @@ static int media_transport_init_sink(struct media_transport *transport)
}
struct media_transport *media_transport_create(struct btd_device *device,
+ const char *remote_endpoint,
uint8_t *configuration,
size_t size, void *data)
{
@@ -849,8 +871,10 @@ struct media_transport *media_transport_create(struct btd_device *device,
transport->configuration = g_new(uint8_t, size);
memcpy(transport->configuration, configuration, size);
transport->size = size;
- transport->path = g_strdup_printf("%s/fd%d", device_get_path(device),
- fd++);
+ transport->remote_endpoint = remote_endpoint;
+ transport->path = g_strdup_printf("%s/fd%d",
+ remote_endpoint ? remote_endpoint :
+ device_get_path(device), fd++);
transport->fd = -1;
uuid = media_endpoint_get_uuid(endpoint);
diff --git a/profiles/audio/transport.h b/profiles/audio/transport.h
index 505ad5c54..ac542bf6c 100644
--- a/profiles/audio/transport.h
+++ b/profiles/audio/transport.h
@@ -25,6 +25,7 @@
struct media_transport;
struct media_transport *media_transport_create(struct btd_device *device,
+ const char *remote_endpoint,
uint8_t *configuration,
size_t size, void *data);
--
2.21.0
From 7ba5782208069a27ed8207b6b43e5ba1b4a5c9c6 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Tue, 8 Jan 2019 10:34:42 -0300
Subject: [PATCH 15/31] a2dp: Cache remote endpoints
In order to always have the Endpoint interface available the remote
endpoints needs to be cached since the remote stack may config a stream
on its own there may not be a chance to discover the endpoits available
which would make it impossible to switch endpoints.
---
profiles/audio/a2dp.c | 724 +++++++++++++++++++++++++----------------
profiles/audio/avdtp.c | 46 ++-
profiles/audio/avdtp.h | 5 +
3 files changed, 492 insertions(+), 283 deletions(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 4fa01894a..6975682c9 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1441,6 +1441,410 @@ static gboolean disconnect_cb(GIOChannel *io, GIOCondition cond, gpointer data)
return FALSE;
}
+static void caps_add_codec(GSList **l, uint8_t codec, uint8_t *caps,
+ size_t size)
+{
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct avdtp_media_codec_capability *cap;
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ *l = g_slist_append(*l, media_transport);
+
+ cap = g_malloc0(sizeof(*cap) + size);
+ cap->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ cap->media_codec_type = codec;
+ memcpy(cap->data, caps, size);
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap,
+ sizeof(*cap) + size);
+
+ *l = g_slist_append(*l, media_codec);
+ g_free(cap);
+}
+
+struct client {
+ const char *sender;
+ const char *path;
+};
+
+static int match_client(const void *data, const void *user_data)
+{
+ struct a2dp_sep *sep = (void *) data;
+ const struct a2dp_endpoint *endpoint = sep->endpoint;
+ const struct client *client = user_data;
+
+ if (strcmp(client->sender, endpoint->get_name(sep, sep->user_data)))
+ return -1;
+
+ return strcmp(client->path, endpoint->get_path(sep, sep->user_data));
+}
+
+static struct a2dp_sep *find_sep(struct a2dp_server *server, uint8_t type,
+ const char *sender, const char *path)
+{
+ GSList *l;
+ struct client client = { sender, path };
+
+ l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
+
+ l = g_slist_find_custom(l, &client, match_client);
+ if (l)
+ return l->data;
+
+ return NULL;
+}
+
+static int parse_properties(DBusMessageIter *props, uint8_t **caps, int *size)
+{
+ while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(props, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+ if (strcasecmp(key, "Capabilities") == 0) {
+ DBusMessageIter array;
+
+ if (var != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&value, &array);
+ dbus_message_iter_get_fixed_array(&array, caps, size);
+ return 0;
+ }
+
+ dbus_message_iter_next(props);
+ }
+
+ return -EINVAL;
+}
+
+static void reconfig_cb(struct avdtp *session, struct a2dp_sep *sep,
+ struct avdtp_stream *stream, int err, void *user_data)
+{
+ DBusMessage *msg = user_data;
+
+ if (err)
+ g_dbus_send_message(btd_get_dbus_connection(),
+ btd_error_failed(msg, strerror(-err)));
+ else
+ g_dbus_send_reply(btd_get_dbus_connection(), msg,
+ DBUS_TYPE_INVALID);
+
+ dbus_message_unref(msg);
+}
+
+static int a2dp_reconfig(struct a2dp_channel *chan, const char *sender,
+ struct a2dp_sep *lsep, struct a2dp_remote_sep *rsep,
+ uint8_t *caps, int size, void *user_data)
+{
+ struct a2dp_setup *setup;
+ struct a2dp_setup_cb *cb_data;
+ GSList *l;
+ int err;
+
+ setup = a2dp_setup_get(chan->session);
+ if (!setup)
+ return -ENOMEM;
+
+ cb_data = setup_cb_new(setup);
+ cb_data->config_cb = reconfig_cb;
+ cb_data->user_data = user_data;
+
+ setup->sep = lsep;
+ setup->rsep = rsep;
+
+ caps_add_codec(&setup->caps, setup->sep->codec, caps, size);
+
+ l = avdtp_get_type(rsep->sep) == AVDTP_SEP_TYPE_SINK ?
+ chan->server->sources :
+ chan->server->sinks;
+
+ /* Check for existing stream and close it */
+ for (; l; l = g_slist_next(l)) {
+ struct a2dp_sep *tmp = l->data;
+
+ /* Attempt to reconfigure if a stream already exists */
+ if (tmp->stream) {
+ /* Only allow switching sep from the same sender */
+ if (strcmp(sender, tmp->endpoint->get_name(tmp,
+ tmp->user_data)))
+ return -EPERM;
+
+ err = avdtp_close(chan->session, tmp->stream, FALSE);
+ if (err < 0) {
+ error("avdtp_close: %s", strerror(-err));
+ goto fail;
+ }
+
+ setup->reconfigure = TRUE;
+
+ return 0;
+ }
+ }
+
+ err = avdtp_set_configuration(setup->session, setup->rsep->sep,
+ lsep->lsep,
+ setup->caps,
+ &setup->stream);
+ if (err < 0) {
+ error("avdtp_set_configuration: %s", strerror(-err));
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ setup_unref(setup);
+ return err;
+}
+
+static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct a2dp_remote_sep *rsep = data;
+ struct a2dp_channel *chan = rsep->chan;
+ struct a2dp_sep *lsep = NULL;
+ struct avdtp_service_capability *service;
+ struct avdtp_media_codec_capability *codec;
+ DBusMessageIter args, props;
+ const char *sender, *path;
+ uint8_t *caps;
+ int err, size = 0;
+
+ sender = dbus_message_get_sender(msg);
+
+ dbus_message_iter_init(msg, &args);
+
+ dbus_message_iter_get_basic(&args, &path);
+ dbus_message_iter_next(&args);
+
+ lsep = find_sep(chan->server, avdtp_get_type(rsep->sep), sender, path);
+ if (!lsep)
+ return btd_error_invalid_args(msg);
+
+ service = avdtp_get_codec(rsep->sep);
+ codec = (struct avdtp_media_codec_capability *) service->data;
+
+ /* Check if codec really matches */
+ if (!endpoint_match_codec_ind(chan->session, codec, lsep))
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&args, &props);
+ if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+ return btd_error_invalid_args(msg);
+
+ if (parse_properties(&props, &caps, &size) < 0)
+ return btd_error_invalid_args(msg);
+
+ err = a2dp_reconfig(chan, sender, lsep, rsep, caps, size,
+ dbus_message_ref(msg));
+ if (err < 0) {
+ dbus_message_unref(msg);
+ return btd_error_failed(msg, strerror(-err));
+ }
+
+ return NULL;
+}
+
+static const GDBusMethodTable sep_methods[] = {
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("SetConfiguration",
+ GDBUS_ARGS({ "endpoint", "o" },
+ { "properties", "a{sv}" } ),
+ NULL, set_configuration) },
+ { },
+};
+
+static gboolean get_uuid(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct a2dp_remote_sep *sep = data;
+ const char *uuid;
+
+ switch (avdtp_get_type(sep->sep)) {
+ case AVDTP_SEP_TYPE_SOURCE:
+ uuid = A2DP_SOURCE_UUID;
+ break;
+ case AVDTP_SEP_TYPE_SINK:
+ uuid = A2DP_SOURCE_UUID;
+ break;
+ default:
+ uuid = "";
+ }
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+ return TRUE;
+}
+
+static gboolean get_codec(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct a2dp_remote_sep *sep = data;
+ struct avdtp_service_capability *cap = avdtp_get_codec(sep->sep);
+ struct avdtp_media_codec_capability *codec = (void *) cap->data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &codec->media_codec_type);
+
+ return TRUE;
+}
+
+static gboolean get_capabilities(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct a2dp_remote_sep *sep = data;
+ struct avdtp_service_capability *service = avdtp_get_codec(sep->sep);
+ struct avdtp_media_codec_capability *codec = (void *) service->data;
+ uint8_t *caps = codec->data;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &caps,
+ service->length - sizeof(*codec));
+
+ dbus_message_iter_close_container(iter, &array);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable sep_properties[] = {
+ { "UUID", "s", get_uuid, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Codec", "y", get_codec, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Capabilities", "ay", get_capabilities, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { }
+};
+
+static void register_remote_sep(void *data, void *user_data)
+{
+ struct avdtp_remote_sep *rsep = data;
+ struct a2dp_channel *chan = user_data;
+ struct a2dp_remote_sep *sep;
+
+ sep = queue_find(chan->seps, match_remote_sep, rsep);
+ if (sep)
+ return;
+
+ sep = new0(struct a2dp_remote_sep, 1);
+ sep->chan = chan;
+ sep->sep = rsep;
+
+ if (!(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL))
+ goto done;
+
+ asprintf(&sep->path, "%s/sep%d", device_get_path(chan->device),
+ avdtp_get_seid(rsep));
+
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ sep->path, MEDIA_ENDPOINT_INTERFACE,
+ sep_methods, NULL, sep_properties,
+ sep, remote_sep_free) == FALSE) {
+ error("Could not register remote sep %s", sep->path);
+ free(sep->path);
+ sep->path = NULL;
+ goto done;
+ }
+
+ DBG("Found remote SEP: %s", sep->path);
+
+done:
+ queue_push_tail(chan->seps, sep);
+}
+
+static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file,
+ char **seids)
+{
+ struct avdtp_remote_sep *sep;
+
+ if (!seids)
+ return;
+
+ for (; *seids; seids++) {
+ uint8_t seid;
+ uint8_t type;
+ uint8_t codec;
+ char *value, caps[256];
+ uint8_t data[128];
+ int i, size;
+ GSList *l = NULL;
+
+ if (sscanf(*seids, "%02hhx", &seid) != 1)
+ continue;
+
+ value = g_key_file_get_string(key_file, "Endpoints", *seids,
+ NULL);
+ if (!value)
+ continue;
+
+ if (sscanf(value, "%02hhx:%02hhx:%s", &type, &codec,
+ caps) != 3) {
+ warn("Unable to load Endpoint: seid %u", seid);
+ g_free(value);
+ continue;
+ }
+
+ for (i = 0, size = strlen(caps); i < size; i += 2) {
+ uint8_t *tmp = data + i / 2;
+
+ if (sscanf(caps + i, "%02hhx", tmp) != 1) {
+ warn("Unable to load Endpoint: seid %u", seid);
+ break;
+ }
+ }
+
+ g_free(value);
+
+ if (i != size)
+ continue;
+
+ caps_add_codec(&l, codec, data, size / 2);
+
+ sep = avdtp_register_remote_sep(chan->session, seid, type, l);
+ if (!sep) {
+ warn("Unable to register Endpoint: seid %u", seid);
+ continue;
+ }
+
+ register_remote_sep(sep, chan);
+ }
+}
+
+static void load_remote_seps(struct a2dp_channel *chan)
+{
+ struct btd_device *device = chan->device;
+ char filename[PATH_MAX];
+ char dst_addr[18];
+ char **keys;
+ GKeyFile *key_file;
+
+ ba2str(device_get_address(device), dst_addr);
+
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s",
+ btd_adapter_get_storage_dir(device_get_adapter(device)),
+ dst_addr);
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+ keys = g_key_file_get_keys(key_file, "Endpoints", NULL, NULL);
+
+ load_remote_sep(chan, key_file, keys);
+
+ g_strfreev(keys);
+ g_key_file_free(key_file);
+}
+
static void avdtp_state_cb(struct btd_device *dev, struct avdtp *session,
avdtp_session_state_t old_state,
avdtp_session_state_t new_state,
@@ -1456,6 +1860,9 @@ static void avdtp_state_cb(struct btd_device *dev, struct avdtp *session,
case AVDTP_SESSION_STATE_CONNECTING:
break;
case AVDTP_SESSION_STATE_CONNECTED:
+ if (!chan->session)
+ chan->session = session;
+ load_remote_seps(chan);
break;
}
}
@@ -1904,28 +2311,6 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
a2dp_unregister_sep(sep);
}
-static void setup_add_caps(struct a2dp_setup *setup, uint8_t *caps, size_t size)
-{
- struct avdtp_service_capability *media_transport, *media_codec;
- struct avdtp_media_codec_capability *cap;
-
- media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
- NULL, 0);
-
- setup->caps = g_slist_append(setup->caps, media_transport);
-
- cap = g_malloc0(sizeof(*cap) + size);
- cap->media_type = AVDTP_MEDIA_TYPE_AUDIO;
- cap->media_codec_type = setup->sep->codec;
- memcpy(cap->data, caps, size);
-
- media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap,
- sizeof(*cap) + size);
-
- setup->caps = g_slist_append(setup->caps, media_codec);
- g_free(cap);
-}
-
static void select_cb(struct a2dp_setup *setup, void *ret, int size)
{
if (size < 0) {
@@ -1933,7 +2318,7 @@ static void select_cb(struct a2dp_setup *setup, void *ret, int size)
goto done;
}
- setup_add_caps(setup, ret, size);
+ caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
done:
finalize_select(setup);
@@ -1989,275 +2374,58 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
return a2dp_find_sep(session, l, NULL);
}
-struct client {
- const char *sender;
- const char *path;
-};
-
-static int match_client(const void *data, const void *user_data)
-{
- struct a2dp_sep *sep = (void *) data;
- const struct a2dp_endpoint *endpoint = sep->endpoint;
- const struct client *client = user_data;
-
- if (strcmp(client->sender, endpoint->get_name(sep, sep->user_data)))
- return -1;
-
- return strcmp(client->path, endpoint->get_path(sep, sep->user_data));
-}
-
-static struct a2dp_sep *find_sep(struct a2dp_server *server, const char *sender,
- const char *path)
-{
- GSList *l;
- struct client client = { sender, path };
-
- l = g_slist_find_custom(server->sources, &client, match_client);
- if (l)
- return l->data;
-
- l = g_slist_find_custom(server->sinks, &client, match_client);
- if (l)
- return l->data;
-
- return NULL;
-}
-
-static int parse_properties(DBusMessageIter *props, uint8_t **caps, int *size)
-{
- while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
- const char *key;
- DBusMessageIter value, entry;
- int var;
-
- dbus_message_iter_recurse(props, &entry);
- dbus_message_iter_get_basic(&entry, &key);
-
- dbus_message_iter_next(&entry);
- dbus_message_iter_recurse(&entry, &value);
-
- var = dbus_message_iter_get_arg_type(&value);
- if (strcasecmp(key, "Capabilities") == 0) {
- DBusMessageIter array;
-
- if (var != DBUS_TYPE_ARRAY)
- return -EINVAL;
-
- dbus_message_iter_recurse(&value, &array);
- dbus_message_iter_get_fixed_array(&array, caps, size);
- return 0;
- }
-
- dbus_message_iter_next(props);
- }
-
- return -EINVAL;
-}
-
-static int a2dp_reconfig(struct a2dp_channel *chan, const char *sender,
- struct a2dp_sep *lsep, struct a2dp_remote_sep *rsep,
- uint8_t *caps, int size)
-{
- struct a2dp_setup *setup;
- const struct queue_entry *entry;
- int err;
-
- setup = a2dp_setup_get(chan->session);
- if (!setup)
- return -ENOMEM;
-
- setup->sep = lsep;
- setup->rsep = rsep;
-
- setup_add_caps(setup, caps, size);
-
- /* Check for existing stream and close it */
- for (entry = queue_get_entries(chan->server->seps); entry;
- entry = entry->next) {
- struct a2dp_sep *tmp = entry->data;
-
- /* Attempt to reconfigure if a stream already exists */
- if (tmp->stream) {
- /* Only allow switching sep from the same sender */
- if (strcmp(sender, tmp->endpoint->get_name(tmp,
- tmp->user_data)))
- return -EPERM;
-
- err = avdtp_close(chan->session, tmp->stream, FALSE);
- if (err < 0) {
- error("avdtp_close: %s", strerror(-err));
- return err;
- }
-
- setup->reconfigure = TRUE;
-
- return 0;
- }
- }
-
- err = avdtp_set_configuration(setup->session, setup->rsep->sep,
- lsep->lsep,
- setup->caps,
- &setup->stream);
- if (err < 0) {
- error("avdtp_set_configuration: %s", strerror(-err));
- return err;
- }
-
- return 0;
-}
-
-static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct a2dp_remote_sep *rsep = data;
- struct a2dp_channel *chan = rsep->chan;
- struct a2dp_sep *lsep;
- struct avdtp_service_capability *service;
- struct avdtp_media_codec_capability *codec;
- DBusMessageIter args, props;
- const char *sender, *path;
- uint8_t *caps;
- int err, size = 0;
-
- sender = dbus_message_get_sender(msg);
-
- dbus_message_iter_init(msg, &args);
-
- dbus_message_iter_get_basic(&args, &path);
- dbus_message_iter_next(&args);
-
- lsep = find_sep(chan->server, sender, path);
- if (!lsep)
- return btd_error_invalid_args(msg);
-
- /* Check if SEPs are no the same role */
- if (avdtp_get_type(rsep->sep) == lsep->type)
- return btd_error_invalid_args(msg);
-
- service = avdtp_get_codec(rsep->sep);
- codec = (struct avdtp_media_codec_capability *) service->data;
-
- /* Check if codec match */
- if (!endpoint_match_codec_ind(chan->session, codec, lsep))
- return btd_error_invalid_args(msg);
-
- dbus_message_iter_recurse(&args, &props);
- if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
- return btd_error_invalid_args(msg);
-
- if (parse_properties(&props, &caps, &size) < 0)
- return btd_error_invalid_args(msg);
-
- err = a2dp_reconfig(chan, sender, lsep, rsep, caps, size);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
-
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-}
-
-static const GDBusMethodTable sep_methods[] = {
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("SetConfiguration",
- GDBUS_ARGS({ "endpoint", "o" },
- { "properties", "a{sv}" } ),
- NULL, set_configuration) },
- { },
-};
-
-static gboolean get_uuid(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct a2dp_remote_sep *sep = data;
- const char *uuid;
-
- switch (avdtp_get_type(sep->sep)) {
- case AVDTP_SEP_TYPE_SOURCE:
- uuid = A2DP_SOURCE_UUID;
- break;
- case AVDTP_SEP_TYPE_SINK:
- uuid = A2DP_SOURCE_UUID;
- break;
- default:
- uuid = "";
- }
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
-
- return TRUE;
-}
-
-static gboolean get_codec(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct a2dp_remote_sep *sep = data;
- struct avdtp_service_capability *cap = avdtp_get_codec(sep->sep);
- struct avdtp_media_codec_capability *codec = (void *) cap->data;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
- &codec->media_codec_type);
-
- return TRUE;
-}
-
-static gboolean get_capabilities(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
+static void store_remote_sep(void *data, void *user_data)
{
struct a2dp_remote_sep *sep = data;
+ GKeyFile *key_file = (void *) user_data;
+ char seid[4], value[256];
struct avdtp_service_capability *service = avdtp_get_codec(sep->sep);
struct avdtp_media_codec_capability *codec = (void *) service->data;
- uint8_t *caps = codec->data;
- DBusMessageIter array;
+ unsigned int i;
+ ssize_t offset;
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_BYTE_AS_STRING, &array);
+ sprintf(seid, "%02hhx", avdtp_get_seid(sep->sep));
- dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &caps,
- service->length - sizeof(*codec));
+ offset = sprintf(value, "%02hhx:%02hhx:", avdtp_get_type(sep->sep),
+ codec->media_codec_type);
- dbus_message_iter_close_container(iter, &array);
+ for (i = 0; i < service->length - sizeof(*codec); i++)
+ offset += sprintf(value + offset, "%02hhx", codec->data[i]);
- return TRUE;
-}
-static const GDBusPropertyTable sep_properties[] = {
- { "UUID", "s", get_uuid, NULL, NULL,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
- { "Codec", "y", get_codec, NULL, NULL,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
- { "Capabilities", "ay", get_capabilities, NULL, NULL,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
- { }
-};
+ g_key_file_set_string(key_file, "Endpoints", seid, value);
+}
-static void register_remote_sep(void *data, void *user_data)
+static void store_remote_seps(struct a2dp_channel *chan)
{
- struct avdtp_remote_sep *rsep = data;
- struct a2dp_setup *setup = user_data;
- struct a2dp_remote_sep *sep;
+ struct btd_device *device = chan->device;
+ char filename[PATH_MAX];
+ char dst_addr[18];
+ GKeyFile *key_file;
+ char *data;
+ gsize length = 0;
- sep = new0(struct a2dp_remote_sep, 1);
- sep->chan = setup->chan;
- sep->sep = rsep;
+ if (queue_isempty(chan->seps))
+ return;
- if (!(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL))
- goto done;
+ ba2str(device_get_address(device), dst_addr);
- asprintf(&sep->path, "%s/sep%d", device_get_path(setup->chan->device),
- avdtp_get_seid(rsep));
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s",
+ btd_adapter_get_storage_dir(device_get_adapter(device)),
+ dst_addr);
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
- if (g_dbus_register_interface(btd_get_dbus_connection(),
- sep->path, MEDIA_ENDPOINT_INTERFACE,
- sep_methods, NULL, sep_properties,
- sep, remote_sep_free) == FALSE) {
- error("Could not register remote sep %s", sep->path);
- free(sep->path);
- sep->path = NULL;
- }
+ /* Remove current endpoints since it might have changed */
+ g_key_file_remove_group(key_file, "Endpoints", NULL);
- DBG("Found remote SEP: %s", sep->path);
+ queue_foreach(chan->seps, store_remote_sep, key_file);
-done:
- queue_push_tail(setup->chan->seps, sep);
+ data = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(filename, data, length, NULL);
+
+ g_free(data);
+ g_key_file_free(key_file);
}
static void discover_cb(struct avdtp *session, GSList *seps,
@@ -2270,8 +2438,10 @@ static void discover_cb(struct avdtp *session, GSList *seps,
setup->seps = seps;
setup->err = err;
- if (!err && queue_isempty(setup->chan->seps))
- g_slist_foreach(seps, register_remote_sep, setup);
+ if (!err) {
+ g_slist_foreach(seps, register_remote_sep, setup->chan);
+ store_remote_seps(setup->chan);
+ }
finalize_discover(setup);
}
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index cc4322d10..4f964feb5 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -2638,12 +2638,15 @@ static gboolean avdtp_discover_resp(struct avdtp *session,
stream = find_stream_by_rseid(session, resp->seps[i].seid);
sep = find_remote_sep(session->seps, resp->seps[i].seid);
- if (!sep) {
- if (resp->seps[i].inuse && !stream)
- continue;
- sep = g_new0(struct avdtp_remote_sep, 1);
- session->seps = g_slist_append(session->seps, sep);
- }
+ if (sep && sep->type == resp->seps[i].type &&
+ sep->media_type == resp->seps[i].media_type)
+ continue;
+
+ if (resp->seps[i].inuse && !stream)
+ continue;
+
+ sep = g_new0(struct avdtp_remote_sep, 1);
+ session->seps = g_slist_append(session->seps, sep);
sep->stream = stream;
sep->seid = resp->seps[i].seid;
@@ -3192,6 +3195,37 @@ struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
return cap;
}
+struct avdtp_remote_sep *avdtp_register_remote_sep(struct avdtp *session,
+ uint8_t seid,
+ uint8_t type,
+ GSList *caps)
+{
+ struct avdtp_remote_sep *sep;
+ GSList *l;
+
+ sep = find_remote_sep(session->seps, seid);
+ if (sep)
+ return sep;
+
+ sep = g_new0(struct avdtp_remote_sep, 1);
+ session->seps = g_slist_append(session->seps, sep);
+ sep->seid = seid;
+ sep->type = type;
+ sep->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ sep->caps = caps;
+
+ for (l = caps; l; l = g_slist_next(l)) {
+ struct avdtp_service_capability *cap = l->data;
+
+ if (cap->category == AVDTP_MEDIA_CODEC)
+ sep->codec = cap;
+ }
+
+ DBG("seid %d type %d media %d", sep->seid, sep->type, sep->media_type);
+
+ return sep;
+}
+
static gboolean process_discover(gpointer data)
{
struct avdtp *session = data;
diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
index e5fc40c89..b03ca9030 100644
--- a/profiles/audio/avdtp.h
+++ b/profiles/audio/avdtp.h
@@ -223,6 +223,11 @@ struct avdtp *avdtp_ref(struct avdtp *session);
struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
void *data, int size);
+struct avdtp_remote_sep *avdtp_register_remote_sep(struct avdtp *session,
+ uint8_t seid,
+ uint8_t type,
+ GSList *caps);
+
uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep);
uint8_t avdtp_get_type(struct avdtp_remote_sep *sep);
--
2.21.0
From 50d87b05b3322f3678b6dda97e4c26ef186f63ac Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Tue, 15 Jan 2019 11:06:04 -0300
Subject: [PATCH 16/31] doc/media-api: Add Device property to MediaEndpoint
This adds Device property which indicates which device the endpoint
belongs to.
---
doc/media-api.txt | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/doc/media-api.txt b/doc/media-api.txt
index 93c3490b6..b909d8e73 100644
--- a/doc/media-api.txt
+++ b/doc/media-api.txt
@@ -533,6 +533,10 @@ Methods void SetConfiguration(object transport, dict properties)
already been unregistered.
+ object Device [readonly, optional]:
+
+ Device object which the endpoint is belongs to.
+
MediaTransport1 hierarchy
=========================
--
2.21.0
From 394b06e7f706de581a4aa50d20232081bceaa824 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Tue, 15 Jan 2019 11:07:13 -0300
Subject: [PATCH 17/31] a2dp: Add implementation of MediaEndpoint.Device
This adds the implementation of MediaEndpoint.Device property so the
clints don't need to guess what device the endpoint belongs.
---
profiles/audio/a2dp.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 6975682c9..ff384cd23 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1718,6 +1718,19 @@ static gboolean get_capabilities(const GDBusPropertyTable *property,
return TRUE;
}
+static gboolean get_device(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct a2dp_remote_sep *sep = data;
+ const char *path;
+
+ path = device_get_path(sep->chan->device);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+ return TRUE;
+}
+
static const GDBusPropertyTable sep_properties[] = {
{ "UUID", "s", get_uuid, NULL, NULL,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
@@ -1725,6 +1738,8 @@ static const GDBusPropertyTable sep_properties[] = {
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ "Capabilities", "ay", get_capabilities, NULL, NULL,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Device", "o", get_device, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ }
};
--
2.21.0
From 4222cbdd5909043c7f903bbca9bb377ba7aeb7bb Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Tue, 22 Jan 2019 15:28:12 +0200
Subject: [PATCH 18/31] a2dp: Add reverse discovery
Now that remote endpoints are exposed there is a chance that a
configured device will reconnect and initiate SetConfiguration skipping
the discovery phase which is now required in order to be able to switch
endpoints, so this introduces the reverse discovery logic in order to
find out about remote endpoints capabilities if they have not been
found yet.
---
profiles/audio/a2dp.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index ff384cd23..4001ea0ea 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -583,6 +583,12 @@ static gboolean endpoint_match_codec_ind(struct avdtp *session,
return TRUE;
}
+static void reverse_discover(struct avdtp *session, GSList *seps, int err,
+ void *user_data)
+{
+ DBG("err %d", err);
+}
+
static gboolean endpoint_setconf_ind(struct avdtp *session,
struct avdtp_local_sep *sep,
struct avdtp_stream *stream,
@@ -638,8 +644,14 @@ static gboolean endpoint_setconf_ind(struct avdtp *session,
setup_ref(setup),
endpoint_setconf_cb,
a2dp_sep->user_data);
- if (ret == 0)
+ if (ret == 0) {
+ /* Attempt to reverve discover if there are no remote
+ * SEPs.
+ */
+ if (queue_isempty(setup->chan->seps))
+ a2dp_discover(session, reverse_discover, NULL);
return TRUE;
+ }
setup_unref(setup);
setup->err = g_new(struct avdtp_error, 1);
--
2.21.0
From 18495cf34e6412af10da2632289cf0c07b5e23ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sat, 26 Jan 2019 11:46:06 +0100
Subject: [PATCH 19/31] a2dp-codecs & avinfo: Simplify defintions and parsing
of aptX family
Reuse whole a2dp_aptx_t structure and defines as they are same for aptX Low
Latency and aptX HD.
---
profiles/audio/a2dp-codecs.h | 46 +++++--------------------------
tools/avinfo.c | 53 ++++++++++--------------------------
2 files changed, 22 insertions(+), 77 deletions(-)
diff --git a/profiles/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h
index 0bdd29110..16088dc26 100644
--- a/profiles/audio/a2dp-codecs.h
+++ b/profiles/audio/a2dp-codecs.h
@@ -212,14 +212,6 @@
#define APTX_LL_VENDOR_ID 0x0000000a
#define APTX_LL_CODEC_ID 0x0002
-#define APTX_LL_CHANNEL_MODE_MONO 0x01
-#define APTX_LL_CHANNEL_MODE_STEREO 0x02
-
-#define APTX_LL_SAMPLING_FREQ_16000 0x08
-#define APTX_LL_SAMPLING_FREQ_32000 0x04
-#define APTX_LL_SAMPLING_FREQ_44100 0x02
-#define APTX_LL_SAMPLING_FREQ_48000 0x01
-
/* Default parameters for aptX Low Latency encoder */
/* Target codec buffer level = 180 */
@@ -243,14 +235,6 @@
#define APTX_HD_VENDOR_ID 0x000000D7
#define APTX_HD_CODEC_ID 0x0024
-#define APTX_HD_CHANNEL_MODE_MONO 0x1
-#define APTX_HD_CHANNEL_MODE_STEREO 0x2
-
-#define APTX_HD_SAMPLING_FREQ_16000 0x8
-#define APTX_HD_SAMPLING_FREQ_32000 0x4
-#define APTX_HD_SAMPLING_FREQ_44100 0x2
-#define APTX_HD_SAMPLING_FREQ_48000 0x1
-
#define LDAC_VENDOR_ID 0x0000012d
#define LDAC_CODEC_ID 0x00aa
@@ -359,25 +343,13 @@ typedef struct {
} __attribute__ ((packed)) a2dp_faststream_t;
typedef struct {
- a2dp_vendor_codec_t info;
- uint8_t channel_mode:4;
- uint8_t frequency:4;
+ a2dp_aptx_t aptx;
uint8_t bidirect_link:1;
uint8_t has_new_caps:1;
uint8_t reserved:6;
a2dp_aptx_ll_new_caps_t new_caps[0];
} __attribute__ ((packed)) a2dp_aptx_ll_t;
-typedef struct {
- a2dp_vendor_codec_t info;
- uint8_t channel_mode:4;
- uint8_t frequency:4;
- uint8_t reserved0;
- uint8_t reserved1;
- uint8_t reserved2;
- uint8_t reserved3;
-} __attribute__ ((packed)) a2dp_aptx_hd_t;
-
#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
__BYTE_ORDER == __BIG_ENDIAN
@@ -429,25 +401,21 @@ typedef struct {
} __attribute__ ((packed)) a2dp_faststream_t;
typedef struct {
- a2dp_vendor_codec_t info;
- uint8_t frequency:4;
- uint8_t channel_mode:4;
+ a2dp_aptx_t aptx;
uint8_t reserved:6;
uint8_t has_new_caps:1;
uint8_t bidirect_link:1;
a2dp_aptx_ll_new_caps_t new_caps[0];
} __attribute__ ((packed)) a2dp_aptx_ll_t;
+#else
+#error "Unknown byte order"
+#endif
+
typedef struct {
- a2dp_vendor_codec_t info;
- uint8_t frequency:4;
- uint8_t channel_mode:4;
+ a2dp_aptx_t aptx;
uint8_t reserved0;
uint8_t reserved1;
uint8_t reserved2;
uint8_t reserved3;
} __attribute__ ((packed)) a2dp_aptx_hd_t;
-
-#else
-#error "Unknown byte order"
-#endif
diff --git a/tools/avinfo.c b/tools/avinfo.c
index 02fc1f233..ea7a93ed2 100644
--- a/tools/avinfo.c
+++ b/tools/avinfo.c
@@ -168,15 +168,8 @@ struct avdtp_content_protection_capability {
uint8_t data[0];
} __attribute__ ((packed));
-static void print_aptx(a2dp_aptx_t *aptx, uint8_t size)
+static void print_aptx_common(a2dp_aptx_t *aptx)
{
- printf("\t\tVendor Specific Value (aptX)");
-
- if (size < sizeof(*aptx)) {
- printf(" (broken)\n");
- return;
- }
-
printf("\n\t\t\tFrequencies: ");
if (aptx->frequency & APTX_SAMPLING_FREQ_16000)
printf("16kHz ");
@@ -192,6 +185,18 @@ static void print_aptx(a2dp_aptx_t *aptx, uint8_t size)
printf("Mono ");
if (aptx->channel_mode & APTX_CHANNEL_MODE_STEREO)
printf("Stereo ");
+}
+
+static void print_aptx(a2dp_aptx_t *aptx, uint8_t size)
+{
+ printf("\t\tVendor Specific Value (aptX)");
+
+ if (size < sizeof(*aptx)) {
+ printf(" (broken)\n");
+ return;
+ }
+
+ print_aptx_common(aptx);
printf("\n");
}
@@ -242,21 +247,7 @@ static void print_aptx_ll(a2dp_aptx_ll_t *aptx_ll, uint8_t size)
return;
}
- printf("\n\t\t\tFrequencies: ");
- if (aptx_ll->frequency & APTX_LL_SAMPLING_FREQ_16000)
- printf("16kHz ");
- if (aptx_ll->frequency & APTX_LL_SAMPLING_FREQ_32000)
- printf("32kHz ");
- if (aptx_ll->frequency & APTX_LL_SAMPLING_FREQ_44100)
- printf("44.1kHz ");
- if (aptx_ll->frequency & APTX_LL_SAMPLING_FREQ_48000)
- printf("48kHz ");
-
- printf("\n\t\t\tChannel modes: ");
- if (aptx_ll->channel_mode & APTX_LL_CHANNEL_MODE_MONO)
- printf("Mono ");
- if (aptx_ll->channel_mode & APTX_LL_CHANNEL_MODE_STEREO)
- printf("Stereo ");
+ print_aptx_common(&aptx_ll->aptx);
printf("\n\t\tBidirectional link: %s",
aptx_ll->bidirect_link ? "Yes" : "No");
@@ -292,21 +283,7 @@ static void print_aptx_hd(a2dp_aptx_hd_t *aptx_hd, uint8_t size)
return;
}
- printf("\n\t\t\tFrequencies: ");
- if (aptx_hd->frequency & APTX_HD_SAMPLING_FREQ_16000)
- printf("16kHz ");
- if (aptx_hd->frequency & APTX_HD_SAMPLING_FREQ_32000)
- printf("32kHz ");
- if (aptx_hd->frequency & APTX_HD_SAMPLING_FREQ_44100)
- printf("44.1kHz ");
- if (aptx_hd->frequency & APTX_HD_SAMPLING_FREQ_48000)
- printf("48kHz ");
-
- printf("\n\t\t\tChannel modes: ");
- if (aptx_hd->channel_mode & APTX_HD_CHANNEL_MODE_MONO)
- printf("Mono ");
- if (aptx_hd->channel_mode & APTX_HD_CHANNEL_MODE_STEREO)
- printf("Stereo ");
+ print_aptx_common(&aptx_hd->aptx);
printf("\n");
}
--
2.21.0
From 5861b20c6bd9c4f82c7b20de1101d9eebc90345b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sat, 26 Jan 2019 11:24:50 +0100
Subject: [PATCH 20/31] avinfo: Dump unknown codecs and unknown categories
---
tools/avinfo.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/tools/avinfo.c b/tools/avinfo.c
index ea7a93ed2..6f9f2763b 100644
--- a/tools/avinfo.c
+++ b/tools/avinfo.c
@@ -625,6 +625,8 @@ static void print_media_codec(
struct avdtp_media_codec_capability *cap,
uint8_t size)
{
+ int i;
+
if (size < sizeof(*cap)) {
printf("\tMedia Codec: Unknown (broken)\n");
return;
@@ -645,6 +647,10 @@ static void print_media_codec(
break;
default:
printf("\tMedia Codec: Unknown\n");
+ printf("\t\tCodec Data:");
+ for (i = 0; i < size - 2; ++i)
+ printf(" 0x%.02x", ((unsigned char *)cap->data)[i]);
+ printf("\n");
}
}
@@ -676,6 +682,7 @@ static void print_content_protection(
static void print_caps(void *data, int size)
{
int processed;
+ int i;
for (processed = 0; processed + 2 < size;) {
struct avdtp_service_capability *cap;
@@ -692,9 +699,15 @@ static void print_caps(void *data, int size)
case AVDTP_REPORTING:
case AVDTP_RECOVERY:
case AVDTP_MULTIPLEXING:
- default:
/* FIXME: Add proper functions */
+ break;
+ default:
printf("\tUnknown category: %d\n", cap->category);
+ printf("\t\tData:");
+ for (i = 0; i < cap->length; ++i)
+ printf(" 0x%.02x",
+ ((unsigned char *)cap->data)[i]);
+ printf("\n");
break;
case AVDTP_MEDIA_CODEC:
print_media_codec((void *) cap->data, cap->length);
--
2.21.0
From e6ad4cbe20f3e66c77fa27c2d8b721a4ccdef58f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Sat, 26 Jan 2019 11:24:15 +0100
Subject: [PATCH 21/31] avinfo: Fix parsing capabilities
Function print_caps() expects capabilities buffer without AVDTP header.
Previously avinfo somehow worked, because AVDTP header looks like
capability header with unknown category which was skipped.
---
tools/avinfo.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/avinfo.c b/tools/avinfo.c
index 6f9f2763b..e45b50918 100644
--- a/tools/avinfo.c
+++ b/tools/avinfo.c
@@ -799,7 +799,7 @@ static ssize_t avdtp_get_caps(int sk, int seid)
return -1;
}
- print_caps(caps, ret);
+ print_caps(caps->caps, ret - sizeof(struct getcap_resp));
return 0;
}
--
2.21.0
From 87805eea93c451aa1b438a2b01967a96f53dce1f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com>
Date: Tue, 29 Jan 2019 18:12:07 +0100
Subject: [PATCH 22/31] a2dp-codecs: Fix SBC_MAX_BITPOOL and add SBC quality
modes
According to A2DP specification; section SBC; Codec Specific Information
Elements; Minimum / Maximum Bitpool Value, range for Bitpool value is from
2 to 250.
A2DP specification also defines bitpool values for two SBC modes: Middle
Quality and High Quality. They depends on channel mode and frequency. So
add definitions for them into a2dp-codecs file too.
File android/hal-audio-sbc.c was updated to use High Quality mode for
chosen frequency.
---
android/hal-audio-sbc.c | 6 +++---
profiles/audio/a2dp-codecs.h | 16 +++++++++++++++-
2 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/android/hal-audio-sbc.c b/android/hal-audio-sbc.c
index fd6c61b95..4df4ec7ff 100644
--- a/android/hal-audio-sbc.c
+++ b/android/hal-audio-sbc.c
@@ -91,7 +91,7 @@ static const a2dp_sbc_t sbc_presets[] = {
.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16,
.min_bitpool = SBC_MIN_BITPOOL,
- .max_bitpool = SBC_MAX_BITPOOL
+ .max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100,
},
{
.frequency = SBC_SAMPLING_FREQ_44100,
@@ -100,7 +100,7 @@ static const a2dp_sbc_t sbc_presets[] = {
.allocation_method = SBC_ALLOCATION_LOUDNESS,
.block_length = SBC_BLOCK_LENGTH_16,
.min_bitpool = SBC_MIN_BITPOOL,
- .max_bitpool = SBC_MAX_BITPOOL
+ .max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100,
},
{
.frequency = SBC_SAMPLING_FREQ_48000,
@@ -109,7 +109,7 @@ static const a2dp_sbc_t sbc_presets[] = {
.allocation_method = SBC_ALLOCATION_LOUDNESS,
.block_length = SBC_BLOCK_LENGTH_16,
.min_bitpool = SBC_MIN_BITPOOL,
- .max_bitpool = SBC_MAX_BITPOOL
+ .max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_48000,
},
};
diff --git a/profiles/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h
index 16088dc26..93e9d3523 100644
--- a/profiles/audio/a2dp-codecs.h
+++ b/profiles/audio/a2dp-codecs.h
@@ -53,8 +53,22 @@
#define SBC_ALLOCATION_SNR (1 << 1)
#define SBC_ALLOCATION_LOUDNESS 1
-#define SBC_MAX_BITPOOL 64
#define SBC_MIN_BITPOOL 2
+#define SBC_MAX_BITPOOL 250
+
+/* Other settings:
+ * Block length = 16
+ * Allocation method = Loudness
+ * Subbands = 8
+ */
+#define SBC_BITPOOL_MQ_MONO_44100 19
+#define SBC_BITPOOL_MQ_MONO_48000 18
+#define SBC_BITPOOL_MQ_JOINT_STEREO_44100 35
+#define SBC_BITPOOL_MQ_JOINT_STEREO_48000 33
+#define SBC_BITPOOL_HQ_MONO_44100 31
+#define SBC_BITPOOL_HQ_MONO_48000 29
+#define SBC_BITPOOL_HQ_JOINT_STEREO_44100 53
+#define SBC_BITPOOL_HQ_JOINT_STEREO_48000 51
#define MPEG_CHANNEL_MODE_MONO (1 << 3)
#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
--
2.21.0
From d23b245146678847d4afa87d8ea9d859935a5a37 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Mon, 8 Apr 2019 12:41:39 +0300
Subject: [PATCH 23/31] a2dp: Fix UUID of remote Sinks
Sinks were being reported as Sources.
---
profiles/audio/a2dp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 4001ea0ea..8f141739c 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1686,7 +1686,7 @@ static gboolean get_uuid(const GDBusPropertyTable *property,
uuid = A2DP_SOURCE_UUID;
break;
case AVDTP_SEP_TYPE_SINK:
- uuid = A2DP_SOURCE_UUID;
+ uuid = A2DP_SINK_UUID;
break;
default:
uuid = "";
--
2.21.0
From 44257aa797796ac3ddc0e01de68df596b6ebf858 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Fri, 3 May 2019 14:49:22 +0300
Subject: [PATCH 24/31] a2dp: Fix useless statement
Checking for NULL path doesn't really matter since NULL is returned
anyway.
---
profiles/audio/a2dp.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 8f141739c..1c9473887 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -2873,8 +2873,7 @@ struct btd_device *a2dp_setup_get_device(struct a2dp_setup *setup)
const char *a2dp_setup_remote_path(struct a2dp_setup *setup)
{
if (setup->rsep) {
- if (setup->rsep->path)
- return setup->rsep->path;
+ return setup->rsep->path;
}
return NULL;
--
2.21.0
From 12e91430886546160f6131c1622a4923adedf3d2 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Tue, 23 Apr 2019 18:46:36 +0300
Subject: [PATCH 25/31] a2dp: Store last used endpoint
This stores the last used endpoint whenever it is considered open and
then prefer to use it when attempting to reconnect.
---
profiles/audio/a2dp.c | 105 ++++++++++++++++++++++++++++++++++++------
1 file changed, 92 insertions(+), 13 deletions(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 1c9473887..d1ed82243 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -147,6 +147,7 @@ struct a2dp_channel {
unsigned int auth_id;
struct avdtp *session;
struct queue *seps;
+ struct a2dp_remote_sep *last_used;
};
static GSList *servers = NULL;
@@ -860,6 +861,60 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
return TRUE;
}
+static bool match_remote_sep(const void *data, const void *user_data)
+{
+ const struct a2dp_remote_sep *sep = data;
+ const struct avdtp_remote_sep *rsep = user_data;
+
+ return sep->sep == rsep;
+}
+
+static void store_last_used(struct a2dp_channel *chan,
+ struct avdtp_remote_sep *rsep)
+{
+ GKeyFile *key_file;
+ char filename[PATH_MAX];
+ char dst_addr[18];
+ char value[4];
+ char *data;
+ size_t len = 0;
+
+ ba2str(device_get_address(chan->device), dst_addr);
+
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s",
+ btd_adapter_get_storage_dir(device_get_adapter(chan->device)),
+ dst_addr);
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+ sprintf(value, "%02hhx", avdtp_get_seid(rsep));
+
+ g_key_file_set_string(key_file, "Endpoints", "LastUsed", value);
+
+ data = g_key_file_to_data(key_file, &len, NULL);
+ g_file_set_contents(filename, data, len, NULL);
+
+ g_free(data);
+ g_key_file_free(key_file);
+}
+
+static void update_last_used(struct a2dp_channel *chan,
+ struct avdtp_stream *stream)
+{
+ struct avdtp_remote_sep *rsep;
+ struct a2dp_remote_sep *sep;
+
+ rsep = avdtp_stream_get_remote_sep(stream);
+
+ /* Update last used */
+ sep = queue_find(chan->seps, match_remote_sep, rsep);
+ if (chan->last_used == sep)
+ return;
+
+ chan->last_used = sep;
+ store_last_used(chan, rsep);
+}
+
static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, struct avdtp_error *err,
void *user_data)
@@ -884,7 +939,8 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
setup->err = err;
if (setup->start)
finalize_resume(setup);
- }
+ } else if (setup->chan)
+ update_last_used(setup->chan, stream);
finalize_config(setup);
@@ -1077,14 +1133,6 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
return TRUE;
}
-static bool match_remote_sep(const void *data, const void *user_data)
-{
- const struct a2dp_remote_sep *sep = data;
- const struct avdtp_remote_sep *rsep = user_data;
-
- return sep->sep == rsep;
-}
-
static struct a2dp_remote_sep *find_remote_sep(struct a2dp_channel *chan,
struct a2dp_sep *sep)
{
@@ -1791,19 +1839,28 @@ done:
queue_push_tail(chan->seps, sep);
}
+static bool match_seid(const void *data, const void *user_data)
+{
+ const struct a2dp_remote_sep *sep = data;
+ const uint8_t *seid = user_data;
+
+ return avdtp_get_seid(sep->sep) == *seid;
+}
+
static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file,
char **seids)
{
struct avdtp_remote_sep *sep;
+ uint8_t seid;
+ char *value;
if (!seids)
return;
for (; *seids; seids++) {
- uint8_t seid;
uint8_t type;
uint8_t codec;
- char *value, caps[256];
+ char caps[256];
uint8_t data[128];
int i, size;
GSList *l = NULL;
@@ -1847,6 +1904,15 @@ static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file,
register_remote_sep(sep, chan);
}
+
+ value = g_key_file_get_string(key_file, "Endpoints", "LastUsed", NULL);
+ if (!value)
+ return;
+
+ if (sscanf(value, "%02hhx", &seid) != 1)
+ return;
+
+ chan->last_used = queue_find(chan->seps, match_seid, &seid);
}
static void load_remote_seps(struct a2dp_channel *chan)
@@ -2355,8 +2421,12 @@ done:
static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
const char *sender)
{
+ struct a2dp_sep *first = NULL;
+ struct a2dp_channel *chan = find_channel(session);
+
for (; list; list = list->next) {
struct a2dp_sep *sep = list->data;
+ struct avdtp_remote_sep *rsep;
/* Use sender's endpoint if available */
if (sender) {
@@ -2370,14 +2440,23 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
continue;
}
- if (avdtp_find_remote_sep(session, sep->lsep) == NULL)
+ rsep = avdtp_find_remote_sep(session, sep->lsep);
+ if (!rsep)
continue;
+ /* Locate last used if set */
+ if (chan->last_used) {
+ if (chan->last_used->sep == rsep)
+ return sep;
+ first = sep;
+ continue;
+ }
+
return sep;
}
- return NULL;
+ return first;
}
static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
--
2.21.0
From 2934144ad10e7d6988cbfd4e3642be275d41c450 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Mon, 29 Apr 2019 13:30:34 +0300
Subject: [PATCH 26/31] a2dp: Fix not calling SelectConfiguration on other
available endpoints
Endpoint may not be able to select a valid configuration but there could
be other endpoints available that could be used, so instead of just
using the first match this collects all the matching endpoints and put
them into a queue (ordered by priority) then proceed to next endpoint
only failing if none of the available endpoits can select a valid
configuration.
---
profiles/audio/a2dp.c | 77 ++++++++++++++++++++++++++++---------------
1 file changed, 50 insertions(+), 27 deletions(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index d1ed82243..f10ba6c81 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -105,6 +105,7 @@ struct a2dp_setup_cb {
struct a2dp_setup {
struct a2dp_channel *chan;
struct avdtp *session;
+ struct queue *eps;
struct a2dp_sep *sep;
struct a2dp_remote_sep *rsep;
struct avdtp_stream *stream;
@@ -2406,23 +2407,44 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
static void select_cb(struct a2dp_setup *setup, void *ret, int size)
{
- if (size < 0) {
- DBG("Endpoint replied an invalid configuration");
+ struct avdtp_service_capability *service;
+ struct avdtp_media_codec_capability *codec;
+ int err;
+
+ if (size) {
+ caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
goto done;
}
- caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
+ setup->sep = queue_pop_head(setup->eps);
+ if (!setup->sep) {
+ error("Unable to select a valid configuration");
+ queue_destroy(setup->eps, NULL);
+ goto done;
+ }
+
+ setup->rsep = find_remote_sep(setup->chan, setup->sep);
+ service = avdtp_get_codec(setup->rsep->sep);
+ codec = (struct avdtp_media_codec_capability *) service->data;
+
+ err = setup->sep->endpoint->select_configuration(setup->sep,
+ codec->data,
+ service->length - sizeof(*codec),
+ setup_ref(setup),
+ select_cb, setup->sep->user_data);
+ if (err == 0)
+ return;
done:
finalize_select(setup);
setup_unref(setup);
}
-static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
+static struct queue *a2dp_find_eps(struct avdtp *session, GSList *list,
const char *sender)
{
- struct a2dp_sep *first = NULL;
struct a2dp_channel *chan = find_channel(session);
+ struct queue *seps = NULL;
for (; list; list = list->next) {
struct a2dp_sep *sep = list->data;
@@ -2444,26 +2466,25 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
if (!rsep)
continue;
- /* Locate last used if set */
- if (chan->last_used) {
- if (chan->last_used->sep == rsep)
- return sep;
- first = sep;
- continue;
- }
+ if (!seps)
+ seps = queue_new();
- return sep;
+ /* Prepend last used so it is preferred over others */
+ if (chan->last_used && chan->last_used->sep == rsep)
+ queue_push_head(seps, sep);
+ else
+ queue_push_tail(seps, sep);
}
- return first;
+ return seps;
}
-static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
+static struct queue *a2dp_select_eps(struct avdtp *session, uint8_t type,
const char *sender)
{
struct a2dp_server *server;
- struct a2dp_sep *sep;
+ struct queue *seps;
GSList *l;
server = find_server(servers, avdtp_get_adapter(session));
@@ -2473,11 +2494,11 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
/* Check sender's seps first */
- sep = a2dp_find_sep(session, l, sender);
- if (sep != NULL)
- return sep;
+ seps = a2dp_find_eps(session, l, sender);
+ if (seps != NULL)
+ return seps;
- return a2dp_find_sep(session, l, NULL);
+ return a2dp_find_eps(session, l, NULL);
}
static void store_remote_sep(void *data, void *user_data)
@@ -2580,13 +2601,13 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
{
struct a2dp_setup *setup;
struct a2dp_setup_cb *cb_data;
- struct a2dp_sep *sep;
+ struct queue *eps;
struct avdtp_service_capability *service;
struct avdtp_media_codec_capability *codec;
int err;
- sep = a2dp_select_sep(session, type, sender);
- if (!sep) {
+ eps = a2dp_select_eps(session, type, sender);
+ if (!eps) {
error("Unable to select SEP");
return 0;
}
@@ -2599,8 +2620,9 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
cb_data->select_cb = cb;
cb_data->user_data = user_data;
- setup->sep = sep;
- setup->rsep = find_remote_sep(setup->chan, sep);
+ setup->eps = eps;
+ setup->sep = queue_pop_head(eps);
+ setup->rsep = find_remote_sep(setup->chan, setup->sep);
if (setup->rsep == NULL) {
error("Could not find remote sep");
@@ -2610,10 +2632,11 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
service = avdtp_get_codec(setup->rsep->sep);
codec = (struct avdtp_media_codec_capability *) service->data;
- err = sep->endpoint->select_configuration(sep, codec->data,
+ err = setup->sep->endpoint->select_configuration(setup->sep,
+ codec->data,
service->length - sizeof(*codec),
setup_ref(setup),
- select_cb, sep->user_data);
+ select_cb, setup->sep->user_data);
if (err == 0)
return cb_data->id;
--
2.21.0
From 05e8894f97f09a2d17f3c9e8f398424218712ba8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20Lowas-Rzechonek?=
<michal.lowas-rzechonek@silvair.com>
Date: Tue, 30 Apr 2019 12:30:00 +0200
Subject: [PATCH 27/31] a2dp: Fixed warn_unused_result warning
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This used to break builds when using maintainer mode via
./bootstrap-configure:
profiles/audio/a2dp.c:1775:2: error: ignoring return value of
asprintf, declared with attribute warn_unused_result
[-Werror=unused-result]
asprintf(&sep->path, "%s/sep%d",
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
---
profiles/audio/a2dp.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index f10ba6c81..11afe0d05 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1821,8 +1821,15 @@ static void register_remote_sep(void *data, void *user_data)
if (!(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL))
goto done;
- asprintf(&sep->path, "%s/sep%d", device_get_path(chan->device),
- avdtp_get_seid(rsep));
+ if (asprintf(&sep->path, "%s/sep%d",
+ device_get_path(chan->device),
+ avdtp_get_seid(rsep)) < 0) {
+ error("Could not allocate path for remote sep %s/sep%d",
+ device_get_path(chan->device),
+ avdtp_get_seid(rsep));
+ sep->path = NULL;
+ goto done;
+ }
if (g_dbus_register_interface(btd_get_dbus_connection(),
sep->path, MEDIA_ENDPOINT_INTERFACE,
--
2.21.0
From 722eeec6864c1f84207431dfcdf4ef2321543d54 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Fri, 3 May 2019 10:51:34 +0300
Subject: [PATCH 28/31] a2dp: Fix crash when endpoint respond with an error
If endpoint respond with an error the callback will be called with size
set to -1 not 0.
---
profiles/audio/a2dp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 11afe0d05..24587b450 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -2418,7 +2418,7 @@ static void select_cb(struct a2dp_setup *setup, void *ret, int size)
struct avdtp_media_codec_capability *codec;
int err;
- if (size) {
+ if (size >= 0) {
caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
goto done;
}
--
2.21.0
From 0c97c4ecbedd0378c88899bc393f299fac048a48 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Fri, 3 May 2019 11:22:40 +0300
Subject: [PATCH 29/31] a2dp: Try aborting when switching endpoints
If ongoing stream cannot be closed try aborting since the setup may
still be ongoing.
---
profiles/audio/a2dp.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 24587b450..36cc55da5 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1257,6 +1257,11 @@ static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
if (!setup)
return;
+ if (setup->reconfigure) {
+ g_timeout_add(RECONFIGURE_TIMEOUT, a2dp_reconfigure, setup);
+ return;
+ }
+
setup_unref(setup);
}
@@ -1642,8 +1647,12 @@ static int a2dp_reconfig(struct a2dp_channel *chan, const char *sender,
err = avdtp_close(chan->session, tmp->stream, FALSE);
if (err < 0) {
- error("avdtp_close: %s", strerror(-err));
- goto fail;
+ err = avdtp_abort(chan->session, tmp->stream);
+ if (err < 0) {
+ error("avdtp_abort: %s",
+ strerror(-err));
+ goto fail;
+ }
}
setup->reconfigure = TRUE;
--
2.21.0
From a71f4e02625f470f2bf5512caec6f157aef12d7e Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Fri, 3 May 2019 11:24:57 +0300
Subject: [PATCH 30/31] a2dp: Update last used on open indication
This updates the last used endpoint also when receiving an open
request from the remote end.
---
profiles/audio/a2dp.c | 55 +++++++++++++++++++++++--------------------
1 file changed, 29 insertions(+), 26 deletions(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 36cc55da5..6d9c26b3d 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -836,32 +836,6 @@ static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
DBG("Source %p: Set_Configuration_Cfm", sep);
}
-static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
- struct avdtp_stream *stream, uint8_t *err,
- void *user_data)
-{
- struct a2dp_sep *a2dp_sep = user_data;
- struct a2dp_setup *setup;
-
- if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
- DBG("Sink %p: Open_Ind", sep);
- else
- DBG("Source %p: Open_Ind", sep);
-
- setup = a2dp_setup_get(session);
- if (!setup)
- return FALSE;
-
- setup->stream = stream;
-
- if (setup->reconfigure)
- setup->reconfigure = FALSE;
-
- finalize_config(setup);
-
- return TRUE;
-}
-
static bool match_remote_sep(const void *data, const void *user_data)
{
const struct a2dp_remote_sep *sep = data;
@@ -916,6 +890,35 @@ static void update_last_used(struct a2dp_channel *chan,
store_last_used(chan, rsep);
}
+static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Open_Ind", sep);
+ else
+ DBG("Source %p: Open_Ind", sep);
+
+ setup = a2dp_setup_get(session);
+ if (!setup)
+ return FALSE;
+
+ setup->stream = stream;
+
+ if (!err && setup->chan)
+ update_last_used(setup->chan, stream);
+
+ if (setup->reconfigure)
+ setup->reconfigure = FALSE;
+
+ finalize_config(setup);
+
+ return TRUE;
+}
+
static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, struct avdtp_error *err,
void *user_data)
--
2.21.0
From fe6895ac63e37894ccaf17d4bec12e496d547c55 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Fri, 3 May 2019 12:41:15 +0300
Subject: [PATCH 31/31] a2dp: Fix reconfiguring when there multiple devices
connected
When there are multiple devices connected streams need to be matched
with the sessions they belong.
---
profiles/audio/a2dp.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 6d9c26b3d..b54c50315 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1648,6 +1648,10 @@ static int a2dp_reconfig(struct a2dp_channel *chan, const char *sender,
tmp->user_data)))
return -EPERM;
+ /* Check if stream is for the channel */
+ if (!avdtp_has_stream(chan->session, tmp->stream))
+ continue;
+
err = avdtp_close(chan->session, tmp->stream, FALSE);
if (err < 0) {
err = avdtp_abort(chan->session, tmp->stream);
--
2.21.0