client/player: Add support for PACS endpoints
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 5 Aug 2020 00:19:43 +0000 (17:19 -0700)
committerAyush Garg <ayush.garg@samsung.com>
Mon, 15 May 2023 09:25:54 +0000 (14:55 +0530)
This adds support for PAC_SINK and PAC_SOURCE endpoints as well as LC3
presets.

Signed-off-by: Manika Shrivastava <manika.sh@samsung.com>
Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
client/player.c
tools/bluetooth-player.c

index 9969442..c435fd1 100644 (file)
@@ -32,6 +32,7 @@
 #include "lib/uuid.h"
 
 #include "profiles/audio/a2dp-codecs.h"
+#include "src/shared/lc3.h"
 
 #include "src/shared/util.h"
 #include "src/shared/shell.h"
@@ -63,6 +64,8 @@ struct endpoint {
        uint8_t codec;
        struct iovec *caps;
        bool auto_accept;
+       uint8_t cig;
+       uint8_t cis;
        char *transport;
        DBusMessage *msg;
 };
@@ -1147,7 +1150,7 @@ struct codec_capabilities {
 
 #define data(args...) ((const unsigned char[]) { args })
 
-#define SBC_DATA(args...) \
+#define CODEC_DATA(args...) \
        { \
                .iov_base = (void *)data(args), \
                .iov_len = sizeof(data(args)), \
@@ -1160,6 +1163,13 @@ struct codec_capabilities {
                .data = _data, \
        }
 
+#define LC3_DATA(_freq, _duration, _chan_count, _len_min, _len_max) \
+       CODEC_DATA(0x03, LC3_FREQ, _freq, _freq >> 8, \
+                  0x02, LC3_DURATION, _duration, \
+                  0x02, LC3_CHAN_COUNT, _chan_count, \
+                  0x05, LC3_FRAME_LEN, _len_min, _len_min >> 8, _len_max, \
+                  _len_max >> 8)
+
 static const struct capabilities {
        const char *uuid;
        uint8_t codec_id;
@@ -1174,7 +1184,7 @@ static const struct capabilities {
         * Bitpool Range: 2-64
         */
        CODEC_CAPABILITIES(A2DP_SOURCE_UUID, A2DP_CODEC_SBC,
-                                       SBC_DATA(0xff, 0xff, 2, 64)),
+                                       CODEC_DATA(0xff, 0xff, 2, 64)),
        /* A2DP SBC Sink:
         *
         * Channel Modes: Mono DualChannel Stereo JointStereo
@@ -1184,13 +1194,45 @@ static const struct capabilities {
         * Bitpool Range: 2-64
         */
        CODEC_CAPABILITIES(A2DP_SINK_UUID, A2DP_CODEC_SBC,
-                                       SBC_DATA(0xff, 0xff, 2, 64)),
+                                       CODEC_DATA(0xff, 0xff, 2, 64)),
+       /* PAC LC3 Sink:
+        *
+        * Frequencies: 8Khz 11Khz 16Khz 22Khz 24Khz 32Khz 44.1Khz 48Khz
+        * Duration: 7.5 ms 10 ms
+        * Channel count: 3
+        * Frame length: 30-240
+        */
+       CODEC_CAPABILITIES(PAC_SINK_UUID, LC3_ID,
+                                       LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY,
+                                               3u, 30, 240)),
+       /* PAC LC3 Source:
+        *
+        * Frequencies: 8Khz 11Khz 16Khz 22Khz 24Khz 32Khz 44.1Khz 48Khz
+        * Duration: 7.5 ms 10 ms
+        * Channel count: 3
+        * Frame length: 30-240
+        */
+       CODEC_CAPABILITIES(PAC_SOURCE_UUID, LC3_ID,
+                                       LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY,
+                                               3u, 30, 240)),
+};
+
+struct codec_qos {
+       uint32_t interval;
+       uint8_t  framing;
+       char *phy;
+       uint16_t sdu;
+       uint8_t  rtn;
+       uint16_t latency;
+       uint32_t delay;
 };
 
 struct codec_preset {
        const char *name;
        const struct iovec data;
+       const struct codec_qos qos;
        bool is_default;
+       uint8_t latency;
 };
 
 #define SBC_PRESET(_name, _data) \
@@ -1215,32 +1257,212 @@ static struct codec_preset sbc_presets[] = {
         * mono, and 512kb/s for two-channel modes.
         */
        SBC_PRESET("MQ_MONO_44_1",
-               SBC_DATA(0x28, 0x15, 2, SBC_BITPOOL_MQ_MONO_44100)),
+               CODEC_DATA(0x28, 0x15, 2, SBC_BITPOOL_MQ_MONO_44100)),
        SBC_PRESET("MQ_MONO_48",
-               SBC_DATA(0x18, 0x15, 2, SBC_BITPOOL_MQ_MONO_48000)),
+               CODEC_DATA(0x18, 0x15, 2, SBC_BITPOOL_MQ_MONO_48000)),
        SBC_PRESET("MQ_STEREO_44_1",
-               SBC_DATA(0x21, 0x15, 2, SBC_BITPOOL_MQ_JOINT_STEREO_44100)),
+               CODEC_DATA(0x21, 0x15, 2, SBC_BITPOOL_MQ_JOINT_STEREO_44100)),
        SBC_PRESET("MQ_STEREO_48",
-               SBC_DATA(0x11, 0x15, 2, SBC_BITPOOL_MQ_JOINT_STEREO_48000)),
+               CODEC_DATA(0x11, 0x15, 2, SBC_BITPOOL_MQ_JOINT_STEREO_48000)),
        SBC_PRESET("HQ_MONO_44_1",
-               SBC_DATA(0x28, 0x15, 2, SBC_BITPOOL_HQ_MONO_44100)),
+               CODEC_DATA(0x28, 0x15, 2, SBC_BITPOOL_HQ_MONO_44100)),
        SBC_PRESET("HQ_MONO_48",
-               SBC_DATA(0x18, 0x15, 2, SBC_BITPOOL_HQ_MONO_48000)),
+               CODEC_DATA(0x18, 0x15, 2, SBC_BITPOOL_HQ_MONO_48000)),
        SBC_DEFAULT_PRESET("HQ_STEREO_44_1",
-               SBC_DATA(0x21, 0x15, 2, SBC_BITPOOL_HQ_JOINT_STEREO_44100)),
+               CODEC_DATA(0x21, 0x15, 2, SBC_BITPOOL_HQ_JOINT_STEREO_44100)),
        SBC_PRESET("HQ_STEREO_48",
-               SBC_DATA(0x11, 0x15, 2, SBC_BITPOOL_HQ_JOINT_STEREO_48000)),
+               CODEC_DATA(0x11, 0x15, 2, SBC_BITPOOL_HQ_JOINT_STEREO_48000)),
        /* Higher bitrates not recommended by A2DP spec, it dual channel to
         * avoid going above 53 bitpool:
         *
         * https://habr.com/en/post/456476/
         * https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/1092
         */
-       SBC_PRESET("XQ_DUAL_44_1", SBC_DATA(0x24, 0x15, 2, 43)),
-       SBC_PRESET("XQ_DUAL_48", SBC_DATA(0x14, 0x15, 2, 39)),
+       SBC_PRESET("XQ_DUAL_44_1", CODEC_DATA(0x24, 0x15, 2, 43)),
+       SBC_PRESET("XQ_DUAL_48", CODEC_DATA(0x14, 0x15, 2, 39)),
        /* Ultra high bitpool that fits in 512 kbps mandatory bitrate */
-       SBC_PRESET("UQ_STEREO_44_1", SBC_DATA(0x21, 0x15, 2, 64)),
-       SBC_PRESET("UQ_STEREO_48", SBC_DATA(0x11, 0x15, 2, 58)),
+       SBC_PRESET("UQ_STEREO_44_1", CODEC_DATA(0x21, 0x15, 2, 64)),
+       SBC_PRESET("UQ_STEREO_48", CODEC_DATA(0x11, 0x15, 2, 58)),
+};
+
+#define QOS_CONFIG(_interval, _framing, _phy, _sdu, _rtn, _latency, _delay) \
+       { \
+               .interval = _interval, \
+               .framing = _framing, \
+               .phy = _phy, \
+               .sdu = _sdu, \
+               .rtn = _rtn, \
+               .latency = _latency, \
+               .delay = _delay, \
+       }
+
+#define QOS_UNFRAMED(_interval, _phy, _sdu, _rtn, _latency, _delay) \
+       QOS_CONFIG(_interval, 0x00, _phy, _sdu, _rtn, _latency, _delay)
+
+#define QOS_FRAMED(_interval, _phy, _sdu, _rtn, _latency, _delay) \
+       QOS_CONFIG(_interval, 0x01, _phy, _sdu, _rtn, _latency, _delay)
+
+#define QOS_UNFRAMED_1M(_interval, _sdu, _rtn, _latency, _delay) \
+       QOS_UNFRAMED(_interval, "1M", _sdu, _rtn, _latency, _delay) \
+
+#define QOS_FRAMED_1M(_interval, _sdu, _rtn, _latency, _delay) \
+       QOS_FRAMED(_interval, "1M", _sdu, _rtn, _latency, _delay) \
+
+#define QOS_UNFRAMED_2M(_interval, _sdu, _rtn, _latency, _delay) \
+       QOS_UNFRAMED(_interval, "2M", _sdu, _rtn, _latency, _delay) \
+
+#define QOS_FRAMED_2M(_interval, _sdu, _rtn, _latency, _delay) \
+       QOS_FRAMED(_interval, "2M", _sdu, _rtn, _latency, _delay) \
+
+#define LC3_7_5_UNFRAMED(_sdu, _rtn, _latency, _delay) \
+       QOS_UNFRAMED(7500u, "2M", _sdu, _rtn, _latency, _delay)
+
+#define LC3_7_5_FRAMED(_sdu, _rtn, _latency, _delay) \
+       QOS_FRAMED(7500u, "2M", _sdu, _rtn, _latency, _delay)
+
+#define LC3_10_UNFRAMED(_sdu, _rtn, _latency, _delay) \
+       QOS_UNFRAMED_2M(10000u, _sdu, _rtn, _latency, _delay)
+
+#define LC3_10_FRAMED(_sdu, _rtn, _latency, _delay) \
+       QOS_FRAMED_2M(10000u, _sdu, _rtn, _latency, _delay)
+
+#define LC3_PRESET_DATA(_freq, _duration, _len) \
+       CODEC_DATA(0x02, LC3_CONFIG_FREQ, _freq, \
+                  0x02, LC3_CONFIG_DURATION, _duration, \
+                  0x03, LC3_CONFIG_FRAME_LEN, _len, _len >> 8)
+
+#define LC3_PRESET_8KHZ(_duration, _len) \
+       LC3_PRESET_DATA(LC3_CONFIG_FREQ_8KHZ, _duration, _len)
+
+#define LC3_PRESET_11KHZ(_duration, _len) \
+       LC3_PRESET_DATA(LC3_CONFIG_FREQ_11KHZ, _duration, _len)
+
+#define LC3_PRESET_16KHZ(_duration, _len) \
+       LC3_PRESET_DATA(LC3_CONFIG_FREQ_16KHZ, _duration, _len)
+
+#define LC3_PRESET_22KHZ(_duration, _len) \
+       LC3_PRESET_DATA(LC3_CONFIG_FREQ_22KHZ, _duration, _len)
+
+#define LC3_PRESET_24KHZ(_duration, _len) \
+       LC3_PRESET_DATA(LC3_CONFIG_FREQ_24KHZ, _duration, _len)
+
+#define LC3_PRESET_32KHZ(_duration, _len) \
+       LC3_PRESET_DATA(LC3_CONFIG_FREQ_32KHZ, _duration, _len)
+
+#define LC3_PRESET_44KHZ(_duration, _len) \
+       LC3_PRESET_DATA(LC3_CONFIG_FREQ_44KHZ, _duration, _len)
+
+#define LC3_PRESET_48KHZ(_duration, _len) \
+       LC3_PRESET_DATA(LC3_CONFIG_FREQ_48KHZ, _duration, _len)
+
+#define LC3_PRESET_LL(_name, _data, _qos) \
+       { \
+               .name = _name, \
+               .data = _data, \
+               .qos = _qos, \
+               .latency = 0x01, \
+       }
+
+#define LC3_PRESET(_name, _data, _qos) \
+       { \
+               .name = _name, \
+               .data = _data, \
+               .qos = _qos, \
+               .latency = 0x02, \
+       }
+
+#define LC3_PRESET_HR(_name, _data, _qos) \
+       { \
+               .name = _name, \
+               .data = _data, \
+               .qos = _qos, \
+               .latency = 0x03, \
+       }
+
+#define LC3_DEFAULT_PRESET(_name, _data, _qos) \
+       { \
+               .name = _name, \
+               .data = _data, \
+               .is_default = true, \
+               .qos = _qos, \
+               .latency = 0x02, \
+       }
+
+static struct codec_preset lc3_presets[] = {
+       /* Table 4.43: QoS configuration support setting requirements */
+       LC3_PRESET("8_1_1",
+                       LC3_PRESET_8KHZ(LC3_CONFIG_DURATION_7_5, 26u),
+                       LC3_7_5_UNFRAMED(26u, 2u, 8u, 40000u)),
+       LC3_PRESET("8_2_1",
+                       LC3_PRESET_8KHZ(LC3_CONFIG_DURATION_10, 30u),
+                       LC3_10_UNFRAMED(30u, 2u, 10u, 40000u)),
+       LC3_PRESET("16_1_1",
+                       LC3_PRESET_16KHZ(LC3_CONFIG_DURATION_7_5, 30u),
+                       LC3_7_5_UNFRAMED(30u, 2u, 8u, 40000u)),
+       LC3_DEFAULT_PRESET("16_2_1",
+                       LC3_PRESET_16KHZ(LC3_CONFIG_DURATION_10, 40u),
+                       LC3_10_UNFRAMED(40u, 2u, 10u, 40000u)),
+       LC3_PRESET("24_1_1",
+                       LC3_PRESET_24KHZ(LC3_CONFIG_DURATION_7_5, 45u),
+                       LC3_7_5_UNFRAMED(45u, 2u, 8u, 40000u)),
+       LC3_PRESET("24_2_1",
+                       LC3_PRESET_24KHZ(LC3_CONFIG_DURATION_10, 60u),
+                       LC3_10_UNFRAMED(60u, 2u, 10u, 40000u)),
+       LC3_PRESET("32_1_1",
+                       LC3_PRESET_32KHZ(LC3_CONFIG_DURATION_7_5, 60u),
+                       LC3_7_5_UNFRAMED(60u, 2u, 8u, 40000u)),
+       LC3_PRESET("32_2_1",
+                       LC3_PRESET_32KHZ(LC3_CONFIG_DURATION_10, 80u),
+                       LC3_10_UNFRAMED(80u, 2u, 10u, 40000u)),
+       LC3_PRESET("44_1_1",
+                       LC3_PRESET_44KHZ(LC3_CONFIG_DURATION_7_5, 98u),
+                       QOS_FRAMED_2M(8163u, 98u, 5u, 24u, 40000u)),
+       LC3_PRESET("44_2_1",
+                       LC3_PRESET_44KHZ(LC3_CONFIG_DURATION_10, 130u),
+                       QOS_FRAMED_2M(10884u, 130u, 5u, 31u, 40000u)),
+       LC3_PRESET("48_1_1",
+                       LC3_PRESET_48KHZ(LC3_CONFIG_DURATION_7_5, 75u),
+                       LC3_7_5_UNFRAMED(75u, 5u, 15u, 40000u)),
+       LC3_PRESET("48_2_1",
+                       LC3_PRESET_48KHZ(LC3_CONFIG_DURATION_10, 100u),
+                       LC3_10_UNFRAMED(100u, 5u, 20u, 40000u)),
+       LC3_PRESET("48_3_1",
+                       LC3_PRESET_48KHZ(LC3_CONFIG_DURATION_7_5, 90u),
+                       LC3_7_5_UNFRAMED(90u, 5u, 15u, 40000u)),
+       LC3_PRESET("48_4_1",
+                       LC3_PRESET_48KHZ(LC3_CONFIG_DURATION_10, 120u),
+                       LC3_10_UNFRAMED(120u, 5u, 20u, 40000u)),
+       LC3_PRESET("48_5_1",
+                       LC3_PRESET_48KHZ(LC3_CONFIG_DURATION_7_5, 117u),
+                       LC3_7_5_UNFRAMED(117u, 5u, 15u, 40000u)),
+       LC3_PRESET("48_6_1",
+                       LC3_PRESET_48KHZ(LC3_CONFIG_DURATION_10, 155u),
+                       LC3_10_UNFRAMED(155u, 5u, 20u, 40000u)),
+       /* QoS Configuration settings for high reliability audio data */
+       LC3_PRESET_HR("44_1_2",
+                       LC3_PRESET_44KHZ(LC3_CONFIG_DURATION_7_5, 98u),
+                       QOS_FRAMED_2M(8163u, 98u, 23u, 54u, 40000u)),
+       LC3_PRESET_HR("44_2_2",
+                       LC3_PRESET_44KHZ(LC3_CONFIG_DURATION_10, 130u),
+                       QOS_FRAMED_2M(10884u, 130u, 23u, 71u, 40000u)),
+       LC3_PRESET_HR("48_1_2",
+                       LC3_PRESET_48KHZ(LC3_CONFIG_DURATION_7_5, 75u),
+                       LC3_7_5_UNFRAMED(75u, 23u, 45u, 40000u)),
+       LC3_PRESET_HR("48_2_2",
+                       LC3_PRESET_48KHZ(LC3_CONFIG_DURATION_10, 100u),
+                       LC3_10_UNFRAMED(100u, 23u, 60u, 40000u)),
+       LC3_PRESET_HR("48_3_2",
+                       LC3_PRESET_48KHZ(LC3_CONFIG_DURATION_7_5, 90u),
+                       LC3_7_5_UNFRAMED(90u, 23u, 45u, 40000u)),
+       LC3_PRESET_HR("48_4_2",
+                       LC3_PRESET_48KHZ(LC3_CONFIG_DURATION_10, 120u),
+                       LC3_10_UNFRAMED(120u, 23u, 60u, 40000u)),
+       LC3_PRESET_HR("48_5_2",
+                       LC3_PRESET_48KHZ(LC3_CONFIG_DURATION_7_5, 117u),
+                       LC3_7_5_UNFRAMED(117u, 23u, 45u, 40000u)),
+       LC3_PRESET_HR("48_6_2",
+                       LC3_PRESET_48KHZ(LC3_CONFIG_DURATION_10, 155u),
+                       LC3_10_UNFRAMED(155u, 23u, 60u, 40000u)),
 };
 
 #define PRESET(_uuid, _presets) \
@@ -1257,6 +1479,8 @@ static const struct preset {
 } presets[] = {
        PRESET(A2DP_SOURCE_UUID, sbc_presets),
        PRESET(A2DP_SINK_UUID, sbc_presets),
+       PRESET(PAC_SINK_UUID, lc3_presets),
+       PRESET(PAC_SOURCE_UUID, lc3_presets),
 };
 
 static struct codec_preset *find_preset(const char *uuid, const char *name)
@@ -1399,10 +1623,7 @@ static DBusMessage *endpoint_select_configuration(DBusConnection *conn,
        }
 
        p = find_preset(ep->uuid, NULL);
-       if (!p)
-               NULL;
-
-       if (p->data.iov_base) {
+       if (!p) {
                reply = g_dbus_create_error(msg, "org.bluez.Error.Rejected",
                                                                NULL);
                return reply;
@@ -1418,6 +1639,190 @@ static DBusMessage *endpoint_select_configuration(DBusConnection *conn,
        return reply;
 }
 
+struct endpoint_config {
+       GDBusProxy *proxy;
+       struct endpoint *ep;
+       struct iovec *caps;
+       uint8_t target_latency;
+       const struct codec_qos *qos;
+};
+
+static void append_properties(DBusMessageIter *iter,
+                                               struct endpoint_config *cfg)
+{
+       DBusMessageIter dict;
+       struct codec_qos *qos = (void *)cfg->qos;
+       const char *key = "Capabilities";
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
+
+       bt_shell_printf("Capabilities: ");
+       bt_shell_hexdump(cfg->caps->iov_base, cfg->caps->iov_len);
+
+       g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &key,
+                                       DBUS_TYPE_BYTE, &cfg->caps->iov_base,
+                                       cfg->caps->iov_len);
+
+       if (!qos)
+               goto done;
+
+       if (cfg->target_latency) {
+               bt_shell_printf("TargetLatency 0x%02x\n", qos->interval);
+               g_dbus_dict_append_entry(&dict, "TargetLatency",
+                                       DBUS_TYPE_BYTE, &cfg->target_latency);
+       }
+
+       if (cfg->ep->cig != BT_ISO_QOS_CIG_UNSET) {
+               bt_shell_printf("CIG 0x%2.2x\n", cfg->ep->cig);
+               g_dbus_dict_append_entry(&dict, "CIG", DBUS_TYPE_BYTE,
+                                                       &cfg->ep->cig);
+       }
+
+       if (cfg->ep->cis != BT_ISO_QOS_CIS_UNSET) {
+               bt_shell_printf("CIS 0x%2.2x\n", cfg->ep->cis);
+               g_dbus_dict_append_entry(&dict, "CIS", DBUS_TYPE_BYTE,
+                                                       &cfg->ep->cis);
+       }
+
+       bt_shell_printf("Interval %u\n", qos->interval);
+
+       g_dbus_dict_append_entry(&dict, "Interval", DBUS_TYPE_UINT32,
+                                               &qos->interval);
+
+       bt_shell_printf("Framing %s\n", qos->framing ? "true" : "false");
+
+       g_dbus_dict_append_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN,
+                                               &qos->framing);
+
+       bt_shell_printf("PHY %s\n", qos->phy);
+
+       g_dbus_dict_append_entry(&dict, "PHY", DBUS_TYPE_STRING, &qos->phy);
+
+       bt_shell_printf("SDU %u\n", cfg->qos->sdu);
+
+       g_dbus_dict_append_entry(&dict, "SDU", DBUS_TYPE_UINT16, &qos->sdu);
+
+       bt_shell_printf("Retransmissions %u\n", qos->rtn);
+
+       g_dbus_dict_append_entry(&dict, "Retransmissions", DBUS_TYPE_BYTE,
+                                               &qos->rtn);
+
+       bt_shell_printf("Latency %u\n", qos->latency);
+
+       g_dbus_dict_append_entry(&dict, "Latency", DBUS_TYPE_UINT16,
+                                               &qos->latency);
+
+       bt_shell_printf("Delay %u\n", qos->delay);
+
+       g_dbus_dict_append_entry(&dict, "Delay", DBUS_TYPE_UINT32,
+                                               &qos->delay);
+
+done:
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static struct iovec *iov_append(struct iovec **iov, const void *data,
+                                                       size_t len)
+{
+       if (!*iov) {
+               *iov = new0(struct iovec, 1);
+               (*iov)->iov_base = new0(uint8_t, UINT8_MAX);
+       }
+
+       if (data && len) {
+               memcpy((*iov)->iov_base + (*iov)->iov_len, data, len);
+               (*iov)->iov_len += len;
+       }
+
+       return *iov;
+}
+
+static DBusMessage *endpoint_select_properties_reply(struct endpoint *ep,
+                                               DBusMessage *msg,
+                                               struct codec_preset *preset)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       struct endpoint_config *cfg;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       cfg = new0(struct endpoint_config, 1);
+       cfg->ep = ep;
+
+       /* Copy capabilities */
+       iov_append(&cfg->caps, preset->data.iov_base, preset->data.iov_len);
+       cfg->target_latency = preset->latency;
+
+       if (preset->qos.phy)
+               /* Set QoS parameters */
+               cfg->qos = &preset->qos;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       append_properties(&iter, cfg);
+
+       free(cfg);
+
+       return reply;
+}
+
+static void select_properties_response(const char *input, void *user_data)
+{
+       struct endpoint *ep = user_data;
+       struct codec_preset *p;
+       DBusMessage *reply;
+
+       p = find_preset(ep->uuid, input);
+       if (p) {
+               reply = endpoint_select_properties_reply(ep, ep->msg, p);
+               goto done;
+       }
+
+       bt_shell_printf("Preset %s not found\n", input);
+       reply = g_dbus_create_error(ep->msg, "org.bluez.Error.Rejected", NULL);
+
+done:
+       g_dbus_send_message(dbus_conn, reply);
+       dbus_message_unref(ep->msg);
+       ep->msg = NULL;
+}
+
+static DBusMessage *endpoint_select_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct endpoint *ep = user_data;
+       struct codec_preset *p;
+       DBusMessageIter args;
+       DBusMessage *reply;
+
+       dbus_message_iter_init(msg, &args);
+
+       bt_shell_printf("Endpoint: SelectProperties\n");
+       print_iter("\t", "Properties", &args);
+
+       if (!ep->auto_accept) {
+               ep->msg = dbus_message_ref(msg);
+               bt_shell_prompt_input("Endpoint", "Enter preset/configuration:",
+                                       select_properties_response, ep);
+               return NULL;
+       }
+
+       p = find_preset(ep->uuid, NULL);
+       if (!p)
+               NULL;
+
+       reply = endpoint_select_properties_reply(ep, msg, p);
+       if (!reply)
+               return NULL;
+
+       bt_shell_printf("Auto Accepting using %s...\n", p->name);
+
+       return reply;
+}
+
 static DBusMessage *endpoint_clear_configuration(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
 {
@@ -1477,7 +1882,12 @@ static const GDBusMethodTable endpoint_methods[] = {
                                        NULL, endpoint_set_configuration) },
        { GDBUS_ASYNC_METHOD("SelectConfiguration",
                                        GDBUS_ARGS({ "caps", "ay" } ),
-                                       NULL, endpoint_select_configuration) },
+                                       GDBUS_ARGS({ "cfg", "ay" } ),
+                                       endpoint_select_configuration) },
+       { GDBUS_ASYNC_METHOD("SelectProperties",
+                                       GDBUS_ARGS({ "properties", "a{sv}" } ),
+                                       GDBUS_ARGS({ "properties", "a{sv}" } ),
+                                       endpoint_select_properties) },
        { GDBUS_ASYNC_METHOD("ClearConfiguration",
                                        GDBUS_ARGS({ "transport", "o" } ),
                                        NULL, endpoint_clear_configuration) },
@@ -1624,18 +2034,64 @@ fail:
 
 }
 
+static void endpoint_cis(const char *input, void *user_data)
+{
+       struct endpoint *ep = user_data;
+       char *endptr = NULL;
+       int value;
+
+       if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) {
+               ep->cis = BT_ISO_QOS_CIS_UNSET;
+       } else {
+               value = strtol(input, &endptr, 0);
+
+               if (!endptr || *endptr != '\0' || value > UINT8_MAX) {
+                       bt_shell_printf("Invalid argument: %s\n", input);
+                       return bt_shell_noninteractive_quit(EXIT_FAILURE);
+               }
+
+               ep->cis = value;
+       }
+
+       endpoint_register(ep);
+}
+
+static void endpoint_cig(const char *input, void *user_data)
+{
+       struct endpoint *ep = user_data;
+       char *endptr = NULL;
+       int value;
+
+       if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) {
+               ep->cig = BT_ISO_QOS_CIG_UNSET;
+       } else {
+               value = strtol(input, &endptr, 0);
+
+               if (!endptr || *endptr != '\0' || value > UINT8_MAX) {
+                       bt_shell_printf("Invalid argument: %s\n", input);
+                       return bt_shell_noninteractive_quit(EXIT_FAILURE);
+               }
+
+               ep->cig = value;
+       }
+
+       bt_shell_prompt_input(ep->path, "CIS (auto/value):", endpoint_cis, ep);
+}
+
 static void endpoint_auto_accept(const char *input, void *user_data)
 {
        struct endpoint *ep = user_data;
 
-       if (!strcasecmp(input, "y") || !strcasecmp(input, "yes"))
+       if (!strcasecmp(input, "y") || !strcasecmp(input, "yes")) {
                ep->auto_accept = true;
-       else if (!strcasecmp(input, "n") || !strcasecmp(input, "no"))
+       } else if (!strcasecmp(input, "n") || !strcasecmp(input, "no")) {
                ep->auto_accept = false;
-       else
+       } else {
                bt_shell_printf("Invalid input for Auto Accept\n");
+               return bt_shell_noninteractive_quit(EXIT_FAILURE);
+       }
 
-       endpoint_register(ep);
+       bt_shell_prompt_input(ep->path, "CIG (auto/value):", endpoint_cig, ep);
 }
 
 static void endpoint_set_capabilities(const char *input, void *user_data)
@@ -1693,22 +2149,6 @@ static const struct capabilities *find_capabilities(const char *uuid,
        return NULL;
 }
 
-static struct iovec *iov_append(struct iovec **iov, const void *data,
-                                                       size_t len)
-{
-       if (!*iov) {
-               *iov = new0(struct iovec, 1);
-               (*iov)->iov_base = new0(uint8_t, UINT8_MAX);
-       }
-
-       if (data && len) {
-               memcpy((*iov)->iov_base + (*iov)->iov_len, data, len);
-               (*iov)->iov_len += len;
-       }
-
-       return *iov;
-}
-
 static void cmd_register_endpoint(int argc, char *argv[])
 {
        struct endpoint *ep;
@@ -1798,31 +2238,14 @@ static void cmd_unregister_endpoint(int argc, char *argv[])
        return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
-struct endpoint_config {
-       GDBusProxy *proxy;
-       struct endpoint *ep;
-       struct iovec *caps;
-};
-
 static void config_endpoint_setup(DBusMessageIter *iter, void *user_data)
 {
        struct endpoint_config *cfg = user_data;
-       DBusMessageIter dict;
-       const char *key = "Capabilities";
 
        dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
                                        &cfg->ep->path);
 
-       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
-
-       bt_shell_printf("Capabilities: ");
-       bt_shell_hexdump(cfg->caps->iov_base, cfg->caps->iov_len);
-
-       g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &key,
-                                       DBUS_TYPE_BYTE, &cfg->caps->iov_base,
-                                       cfg->caps->iov_len);
-
-       dbus_message_iter_close_container(iter, &dict);
+       append_properties(iter, cfg);
 }
 
 static void config_endpoint_reply(DBusMessage *message, void *user_data)
@@ -1905,6 +2328,9 @@ static void cmd_config_endpoint(int argc, char *argv[])
                iov_append(&cfg->caps, preset->data.iov_base,
                                                preset->data.iov_len);
 
+               /* Set QoS parameters */
+               cfg->qos = &preset->qos;
+
                endpoint_set_config(cfg);
                return;
        }
@@ -2361,7 +2787,7 @@ static void transport_property_changed(GDBusProxy *proxy, const char *name,
                return;
 
        if (ep->auto_accept) {
-               bt_shell_printf("Auto Accepting...\n");
+               bt_shell_printf("Auto Acquiring...\n");
                if (!g_dbus_proxy_method_call(proxy, "Acquire", NULL,
                                                acquire_reply, proxy, NULL))
                        bt_shell_printf("Failed acquire transport\n");
@@ -2430,6 +2856,15 @@ static void cmd_show_transport(int argc, char *argv[])
        print_property(proxy, "Volume");
        print_property(proxy, "Endpoint");
 
+       print_property(proxy, "Interval");
+       print_property(proxy, "Framing");
+       print_property(proxy, "SDU");
+       print_property(proxy, "Retransmissions");
+       print_property(proxy, "Latency");
+       print_property(proxy, "Location");
+       print_property(proxy, "Metadata");
+       print_property(proxy, "Links");
+
        return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
@@ -2449,23 +2884,27 @@ static struct transport *find_transport(GDBusProxy *proxy)
 static void cmd_acquire_transport(int argc, char *argv[])
 {
        GDBusProxy *proxy;
+       int i;
 
-       proxy = g_dbus_proxy_lookup(transports, NULL, argv[1],
+       for (i = 1; i < argc; i++) {
+               proxy = g_dbus_proxy_lookup(transports, NULL, argv[i],
                                        BLUEZ_MEDIA_TRANSPORT_INTERFACE);
-       if (!proxy) {
-               bt_shell_printf("Transport %s not found\n", argv[1]);
-               return bt_shell_noninteractive_quit(EXIT_FAILURE);
-       }
+               if (!proxy) {
+                       bt_shell_printf("Transport %s not found\n", argv[i]);
+                       return bt_shell_noninteractive_quit(EXIT_FAILURE);
+               }
 
-       if (find_transport(proxy)) {
-               bt_shell_printf("Transport %s already acquired\n", argv[1]);
-               return bt_shell_noninteractive_quit(EXIT_FAILURE);
-       }
+               if (find_transport(proxy)) {
+                       bt_shell_printf("Transport %s already acquired\n",
+                                       argv[i]);
+                       return bt_shell_noninteractive_quit(EXIT_FAILURE);
+               }
 
-       if (!g_dbus_proxy_method_call(proxy, "Acquire", NULL,
-                                       acquire_reply, proxy, NULL)) {
-               bt_shell_printf("Failed acquire transport\n");
-               return bt_shell_noninteractive_quit(EXIT_FAILURE);
+               if (!g_dbus_proxy_method_call(proxy, "Acquire", NULL,
+                                               acquire_reply, proxy, NULL)) {
+                       bt_shell_printf("Failed acquire transport\n");
+                       return bt_shell_noninteractive_quit(EXIT_FAILURE);
+               }
        }
 
        return bt_shell_noninteractive_quit(EXIT_SUCCESS);
@@ -2495,25 +2934,29 @@ static void release_reply(DBusMessage *message, void *user_data)
 static void cmd_release_transport(int argc, char *argv[])
 {
        GDBusProxy *proxy;
-       struct transport *transport;
+       int i;
 
-       proxy = g_dbus_proxy_lookup(transports, NULL, argv[1],
+       for (i = 1; i < argc; i++) {
+               struct transport *transport;
+
+               proxy = g_dbus_proxy_lookup(transports, NULL, argv[i],
                                        BLUEZ_MEDIA_TRANSPORT_INTERFACE);
-       if (!proxy) {
-               bt_shell_printf("Transport %s not found\n", argv[1]);
-               return bt_shell_noninteractive_quit(EXIT_FAILURE);
-       }
+               if (!proxy) {
+                       bt_shell_printf("Transport %s not found\n", argv[1]);
+                       return bt_shell_noninteractive_quit(EXIT_FAILURE);
+               }
 
-       transport = find_transport(proxy);
-       if (!transport) {
-               bt_shell_printf("Transport %s not acquired\n", argv[1]);
-               return bt_shell_noninteractive_quit(EXIT_FAILURE);
-       }
+               transport = find_transport(proxy);
+               if (!transport) {
+                       bt_shell_printf("Transport %s not acquired\n", argv[i]);
+                       return bt_shell_noninteractive_quit(EXIT_FAILURE);
+               }
 
-       if (!g_dbus_proxy_method_call(proxy, "Release", NULL,
+               if (!g_dbus_proxy_method_call(proxy, "Release", NULL,
                                        release_reply, transport, NULL)) {
-               bt_shell_printf("Failed release transport\n");
-               return bt_shell_noninteractive_quit(EXIT_FAILURE);
+                       bt_shell_printf("Failed release transport\n");
+                       return bt_shell_noninteractive_quit(EXIT_FAILURE);
+               }
        }
 
        return bt_shell_noninteractive_quit(EXIT_SUCCESS);
@@ -2706,10 +3149,10 @@ static const struct bt_shell_menu transport_menu = {
        { "show",        "<transport>", cmd_show_transport,
                                                "Transport information",
                                                transport_generator },
-       { "acquire",     "<transport>", cmd_acquire_transport,
+       { "acquire",     "<transport> [transport1...]", cmd_acquire_transport,
                                                "Acquire Transport",
                                                transport_generator },
-       { "release",     "<transport>", cmd_release_transport,
+       { "release",     "<transport> [transport1...]", cmd_release_transport,
                                                "Release Transport",
                                                transport_generator },
        { "send",        "<transport> <filename>", cmd_send_transport,
index 0893662..238a9f3 100755 (executable)
@@ -25,7 +25,6 @@
 #include <glib.h>
 
 #include "gdbus/gdbus.h"
-
 #include "lib/bluetooth.h"
 #include "lib/uuid.h"
 
@@ -36,6 +35,7 @@
 #define PROMPT_ON      COLOR_BLUE "[bluetooth]" COLOR_OFF "# "
 #define PROMPT_OFF     "[bluetooth]# "
 
+
 static DBusConnection *dbus_conn;
 
 static void connect_handler(DBusConnection *connection, void *user_data)