la, 2023-10-28 kello 17:39 +0300, Pauli Virtanen kirjoitti: > Enable the client endpoint to implement SelectQoS() to configure > the QoS as a second step in the configuration flow. > > Remove the QoS parameter from SelectProperties(), as the values > are not actually know at that point of the configuration flow. > > If the client does not implement SelectQoS() we will just use all the > QoS values returned by SelectProperties(). If they are one of the > mandatory configurations, then maybe devices will accept them. > --- > profiles/audio/bap.c | 98 +++++++++++++------- > profiles/audio/media.c | 201 +++++++++++++++++++++++++++++++++-------- > 2 files changed, 225 insertions(+), 74 deletions(-) > > diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c > index b74498c4c..a289daf15 100644 > --- a/profiles/audio/bap.c > +++ b/profiles/audio/bap.c > @@ -725,23 +725,17 @@ fail: > return -EINVAL; > } > > -static void qos_cb(struct bt_bap_stream *stream, uint8_t code, uint8_t reason, > - void *user_data) > +static void ep_reply_msg(struct bap_ep *ep, const char *error) > { > - struct bap_ep *ep = user_data; > DBusMessage *reply; > > - DBG("stream %p code 0x%02x reason 0x%02x", stream, code, reason); > - > - ep->id = 0; > - > if (!ep->msg) > return; > > - if (!code) > + if (!error) > reply = dbus_message_new_method_return(ep->msg); > else > - reply = btd_error_failed(ep->msg, "Unable to configure"); > + reply = btd_error_failed(ep->msg, error); > > g_dbus_send_message(btd_get_dbus_connection(), reply); > > @@ -749,28 +743,30 @@ static void qos_cb(struct bt_bap_stream *stream, uint8_t code, uint8_t reason, > ep->msg = NULL; > } > > -static void config_cb(struct bt_bap_stream *stream, > - uint8_t code, uint8_t reason, > +static void qos_cb(struct bt_bap_stream *stream, uint8_t code, uint8_t reason, > void *user_data) > { > struct bap_ep *ep = user_data; > - DBusMessage *reply; > > DBG("stream %p code 0x%02x reason 0x%02x", stream, code, reason); > > ep->id = 0; > > - if (!code) > - return; > + ep_reply_msg(ep, code ? "Unable to configure" : NULL); > +} > > - if (!ep->msg) > - return; > +static void config_cb(struct bt_bap_stream *stream, > + uint8_t code, uint8_t reason, > + void *user_data) > +{ > + struct bap_ep *ep = user_data; > > - reply = btd_error_failed(ep->msg, "Unable to configure"); > - g_dbus_send_message(btd_get_dbus_connection(), reply); > + DBG("stream %p code 0x%02x reason 0x%02x", stream, code, reason); > > - dbus_message_unref(ep->msg); > - ep->msg = NULL; > + ep->id = 0; > + > + if (code) > + ep_reply_msg(ep, "Unable to configure"); > } > > static void bap_io_close(struct bap_ep *ep) > @@ -1202,7 +1198,7 @@ static void bap_config(void *data, void *user_data) > bt_bap_stream_set_user_data(ep->stream, ep->path); > } > > -static void select_cb(struct bt_bap_pac *pac, int err, struct iovec *caps, > +static void select_codec_cb(struct bt_bap_pac *pac, int err, struct iovec *caps, > struct iovec *metadata, struct bt_bap_qos *qos, > void *user_data) > { > @@ -1252,7 +1248,7 @@ static bool pac_found(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, > > /* TODO: Cache LRU? */ > if (btd_service_is_initiator(service)) { > - if (!bt_bap_select(lpac, rpac, select_cb, ep)) > + if (!bt_bap_select_codec(lpac, rpac, select_codec_cb, ep)) > ep->data->selecting++; > } > > @@ -1877,6 +1873,36 @@ static void bap_create_io(struct bap_data *data, struct bap_ep *ep, > } > } > > +static void select_qos_cb(struct bt_bap_stream *stream, int err, > + struct bt_bap_qos *qos, void *user_data) > +{ > + struct bap_ep *ep = user_data; > + > + DBG("stream %p err %d qos %p", stream, err, qos); > + > + if (err || ep->id) > + goto fail; > + > + if (qos) > + ep->qos = *qos; > + > + bap_create_io(ep->data, ep, stream, true); This uses old QoS values, needs to be fixed. For v2. > + if (!ep->io) { > + error("Unable to create io"); > + goto fail; > + } > + > + ep->id = bt_bap_stream_qos(stream, &ep->qos, qos_cb, ep); > + if (!ep->id) > + goto fail; > + > + return; > + > +fail: > + error("Failed to Configure QoS"); > + ep_reply_msg(ep, "Unable to configure"); > +} > + > static void bap_state(struct bt_bap_stream *stream, uint8_t old_state, > uint8_t new_state, void *user_data) > { > @@ -1902,25 +1928,27 @@ static void bap_state(struct bt_bap_stream *stream, uint8_t old_state, > queue_remove(data->streams, stream); > break; > case BT_BAP_STREAM_STATE_CONFIG: > - if (ep && !ep->id) { > + if (!ep || ep->id) > + break; > + > + switch (bt_bap_stream_get_type(stream)) { > + case BT_BAP_STREAM_TYPE_UCAST: > + if (bt_bap_stream_select_qos(stream, > + select_qos_cb, ep)) { > + error("Failed to Configure QoS"); > + bt_bap_stream_release(stream, > + NULL, NULL); > + return; > + } > + break; > + case BT_BAP_STREAM_TYPE_BCAST: > bap_create_io(data, ep, stream, true); > if (!ep->io) { > error("Unable to create io"); > bt_bap_stream_release(stream, NULL, NULL); > return; > } > - > - if (bt_bap_stream_get_type(stream) == > - BT_BAP_STREAM_TYPE_UCAST) { > - /* Wait QoS response to respond */ > - ep->id = bt_bap_stream_qos(stream, &ep->qos, > - qos_cb, ep); > - if (!ep->id) { > - error("Failed to Configure QoS"); > - bt_bap_stream_release(stream, > - NULL, NULL); > - } > - } > + break; > } > break; > case BT_BAP_STREAM_STATE_QOS: > diff --git a/profiles/audio/media.c b/profiles/audio/media.c > index 4d9a6aa03..42bc21386 100644 > --- a/profiles/audio/media.c > +++ b/profiles/audio/media.c > @@ -318,6 +318,17 @@ static void endpoint_reply(DBusPendingCall *call, void *user_data) > > dbus_error_init(&err); > if (dbus_set_error_from_message(&err, reply)) { > + /* Endpoint is not required to implement SelectQoS */ > + if (dbus_error_has_name(&err, DBUS_ERROR_UNKNOWN_METHOD) && > + dbus_message_is_method_call(request->msg, > + MEDIA_ENDPOINT_INTERFACE, "SelectQoS")) { > + dbus_error_free(&err); > + value = FALSE; > + size = sizeof(value); > + ret = &value; > + goto done; > + } > + > error("Endpoint replied with an error: %s", > err.name); > > @@ -358,6 +369,13 @@ static void endpoint_reply(DBusPendingCall *call, void *user_data) > dbus_message_iter_recurse(&args, &props); > ret = &props; > goto done; > + } else if (dbus_message_is_method_call(request->msg, > + MEDIA_ENDPOINT_INTERFACE, > + "SelectQoS")) { > + dbus_message_iter_init(reply, &args); > + dbus_message_iter_recurse(&args, &props); > + ret = &props; > + goto done; > } else if (!dbus_message_get_args(reply, &err, DBUS_TYPE_INVALID)) { > error("Wrong reply signature: %s", err.message); > dbus_error_free(&err); > @@ -725,9 +743,9 @@ static bool endpoint_init_a2dp_sink(struct media_endpoint *endpoint, int *err) > return true; > } > > -struct pac_select_data { > +struct pac_select_codec_data { > struct bt_bap_pac *pac; > - bt_bap_pac_select_t cb; > + bt_bap_pac_select_codec_t cb; > void *user_data; > }; > > @@ -881,10 +899,10 @@ fail: > return -EINVAL; > } > > -static void pac_select_cb(struct media_endpoint *endpoint, void *ret, int size, > - void *user_data) > +static void pac_select_codec_cb(struct media_endpoint *endpoint, void *ret, > + int size, void *user_data) > { > - struct pac_select_data *data = user_data; > + struct pac_select_codec_data *data = user_data; > DBusMessageIter *iter = ret; > int err; > struct iovec caps, meta; > @@ -920,15 +938,15 @@ done: > data->cb(data->pac, err, &caps, &meta, &qos, data->user_data); > } > > -static int pac_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, > - struct bt_bap_pac_qos *qos, > - bt_bap_pac_select_t cb, void *cb_data, void *user_data) > +static int pac_select_codec(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, > + bt_bap_pac_select_codec_t cb, void *cb_data, > + void *user_data) > { > struct media_endpoint *endpoint = user_data; > struct iovec *caps; > struct iovec *metadata; > const char *endpoint_path; > - struct pac_select_data *data; > + struct pac_select_codec_data *data; > DBusMessage *msg; > DBusMessageIter iter, dict; > const char *key = "Capabilities"; > @@ -946,7 +964,7 @@ static int pac_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, > return -ENOMEM; > } > > - data = new0(struct pac_select_data, 1); > + data = new0(struct pac_select_codec_data, 1); > data->pac = lpac; > data->cb = cb; > data->user_data = cb_data; > @@ -977,47 +995,151 @@ static int pac_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, > metadata->iov_len); > } > > - if (qos && qos->phy) { > - DBusMessageIter entry, variant, qos_dict; > + dbus_message_iter_close_container(&iter, &dict); > + > + return media_endpoint_async_call(msg, endpoint, NULL, > + pac_select_codec_cb, data, free); > +} > + > +struct pac_select_qos_data { > + struct bt_bap_stream *stream; > + bt_bap_pac_select_qos_t cb; > + void *user_data; > +}; > + > +static void pac_select_qos_cb(struct media_endpoint *endpoint, void *ret, > + int size, void *user_data) > +{ > + struct pac_select_qos_data *data = user_data; > + DBusMessageIter *iter = ret; > + int err; > + struct bt_bap_qos qos; > + > + if (!ret) { > + data->cb(data->stream, -EPERM, NULL, data->user_data); > + return; > + } else if (size > 0) { > + /* Endpoint doesn't implement the method, use old values */ > + data->cb(data->stream, 0, NULL, data->user_data); > + return; > + } > + > + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) { > + DBG("Unexpected argument type: %c != %c", > + dbus_message_iter_get_arg_type(iter), > + DBUS_TYPE_DICT_ENTRY); > + data->cb(data->stream, -EINVAL, NULL, data->user_data); > + return; > + } > + > + memset(&qos, 0, sizeof(qos)); > + > + /* Mark CIG and CIS to be auto assigned */ > + qos.ucast.cig_id = BT_ISO_QOS_CIG_UNSET; > + qos.ucast.cis_id = BT_ISO_QOS_CIS_UNSET; > + > + err = parse_select_properties(iter, NULL, NULL, &qos); > + if (err < 0) > + DBG("Unable to parse properties"); > + > + data->cb(data->stream, err, &qos, data->user_data); > +} > > - key = "QoS"; > - dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, > - NULL, &entry); > - dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > - dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > - "a{sv}", &variant); > - dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > - "{sv}", &qos_dict); > +static int pac_select_qos(struct bt_bap_stream *stream, > + struct bt_bap_pac_qos *qos, bt_bap_pac_select_qos_t cb, > + void *cb_data, void *user_data) > +{ > + struct media_endpoint *endpoint = user_data; > + struct bt_bap_pac *rpac; > + const char *endpoint_path; > + struct pac_select_qos_data *data; > + struct iovec *caps, *metadata; > + DBusMessage *msg; > + DBusMessageIter iter, dict; > + DBusMessageIter entry, variant, qos_dict; > + const char *key = "Capabilities"; > + > + rpac = bt_bap_stream_get_rpac(stream); > + if (!rpac) > + return -EINVAL; > > - g_dbus_dict_append_entry(&qos_dict, "Framing", DBUS_TYPE_BYTE, > - &qos->framing); > + caps = bt_bap_stream_get_config(stream); > + if (!caps) > + return -EINVAL; > > - g_dbus_dict_append_entry(&qos_dict, "PHY", DBUS_TYPE_BYTE, > - &qos->phy); > + msg = dbus_message_new_method_call(endpoint->sender, endpoint->path, > + MEDIA_ENDPOINT_INTERFACE, > + "SelectQoS"); > + if (msg == NULL) { > + error("Couldn't allocate D-Bus message"); > + return -ENOMEM; > + } > > - g_dbus_dict_append_entry(&qos_dict, "MaximumLatency", > - DBUS_TYPE_UINT16, &qos->latency); > + data = new0(struct pac_select_qos_data, 1); > + data->stream = stream; > + data->cb = cb; > + data->user_data = cb_data; > > - g_dbus_dict_append_entry(&qos_dict, "MinimumDelay", > - DBUS_TYPE_UINT32, &qos->pd_min); > + dbus_message_iter_init_append(msg, &iter); > > - g_dbus_dict_append_entry(&qos_dict, "MaximumDelay", > - DBUS_TYPE_UINT32, &qos->pd_max); > + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict); > > - g_dbus_dict_append_entry(&qos_dict, "PreferredMinimumDelay", > - DBUS_TYPE_UINT32, &qos->ppd_min); > + endpoint_path = bt_bap_pac_get_user_data(rpac); > + if (endpoint_path) > + g_dbus_dict_append_entry(&dict, "Endpoint", > + DBUS_TYPE_OBJECT_PATH, &endpoint_path); > > - g_dbus_dict_append_entry(&qos_dict, "PreferredMaximumDelay", > - DBUS_TYPE_UINT32, &qos->ppd_max); > + key = "Capabilities"; > + g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &key, > + DBUS_TYPE_BYTE, &caps->iov_base, > + caps->iov_len); > > - dbus_message_iter_close_container(&variant, &qos_dict); > - dbus_message_iter_close_container(&entry, &variant); > - dbus_message_iter_close_container(&dict, &entry); > + metadata = bt_bap_stream_get_metadata(stream); > + if (metadata) { > + key = "Metadata"; > + g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &key, > + DBUS_TYPE_BYTE, > + &metadata->iov_base, > + metadata->iov_len); > } > > + key = "QoS"; > + dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, > + NULL, &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > + "a{sv}", &variant); > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > + "{sv}", &qos_dict); > + > + g_dbus_dict_append_entry(&qos_dict, "Framing", DBUS_TYPE_BYTE, > + &qos->framing); > + > + g_dbus_dict_append_entry(&qos_dict, "PHY", DBUS_TYPE_BYTE, > + &qos->phy); > + > + g_dbus_dict_append_entry(&qos_dict, "MaximumLatency", > + DBUS_TYPE_UINT16, &qos->latency); > + > + g_dbus_dict_append_entry(&qos_dict, "MinimumDelay", > + DBUS_TYPE_UINT32, &qos->pd_min); > + > + g_dbus_dict_append_entry(&qos_dict, "MaximumDelay", > + DBUS_TYPE_UINT32, &qos->pd_max); > + > + g_dbus_dict_append_entry(&qos_dict, "PreferredMinimumDelay", > + DBUS_TYPE_UINT32, &qos->ppd_min); > + > + g_dbus_dict_append_entry(&qos_dict, "PreferredMaximumDelay", > + DBUS_TYPE_UINT32, &qos->ppd_max); > + > + dbus_message_iter_close_container(&variant, &qos_dict); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(&dict, &entry); > + > dbus_message_iter_close_container(&iter, &dict); > > - return media_endpoint_async_call(msg, endpoint, NULL, pac_select_cb, > + return media_endpoint_async_call(msg, endpoint, NULL, pac_select_qos_cb, > data, free); > } > > @@ -1187,8 +1309,9 @@ static void pac_clear(struct bt_bap_stream *stream, void *user_data) > } > > static struct bt_bap_pac_ops pac_ops = { > - .select = pac_select, > + .select_codec = pac_select_codec, > .config = pac_config, > + .select_qos = pac_select_qos, > .clear = pac_clear, > }; >