4715 lines
135 KiB
Diff
4715 lines
135 KiB
Diff
|
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
|
|||
|
|