linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete
@ 2022-04-11 23:58 Luiz Augusto von Dentz
  2022-04-11 23:58 ` [PATCH BlueZ 1/2] client/player: Add endpoint menu Luiz Augusto von Dentz
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2022-04-11 23:58 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This fixes not clenup ssp_status and ssp_auto_complete flags on
auth_complete.
---
 emulator/btdev.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/emulator/btdev.c b/emulator/btdev.c
index f70fa9136..ccfaed36b 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -1366,6 +1366,11 @@ static void auth_complete(struct btdev_conn *conn, uint8_t status)
 	ev.status = status;
 
 	send_event(conn->dev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
+
+	conn->dev->ssp_status = 0;
+	conn->dev->ssp_auth_complete = false;
+	conn->link->dev->ssp_status = 0;
+	conn->link->dev->ssp_auth_complete = false;
 }
 
 static int cmd_link_key_reply_complete(struct btdev *dev, const void *data,
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH BlueZ 1/2] client/player: Add endpoint menu
  2022-04-11 23:58 [PATCH BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete Luiz Augusto von Dentz
@ 2022-04-11 23:58 ` Luiz Augusto von Dentz
  2022-04-12  2:52   ` [BlueZ,1/2] " bluez.test.bot
  2022-04-11 23:58 ` [PATCH BlueZ 2/2] client/player: Add transport menu Luiz Augusto von Dentz
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2022-04-11 23:58 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds endpoint menu:

[bluetooth]# menu endpoint
Menu endpoint:
Available commands:
-------------------
list [local]                                      List available endpoints
show <endpoint>                                   Endpoint information
register <UUID> <codec> [capabilities...]         Register Endpoint
unregister <UUID/object>                          Register Endpoint
config <endpoint> <local endpoint> [preset]       Configure Endpoint
---
 client/player.c | 990 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 985 insertions(+), 5 deletions(-)

diff --git a/client/player.c b/client/player.c
index 1095482d4..b0a8b2c56 100644
--- a/client/player.c
+++ b/client/player.c
@@ -21,7 +21,9 @@
 #include <stdlib.h>
 #include <fcntl.h>
 #include <string.h>
+#include <sys/ioctl.h>
 #include <sys/uio.h>
+#include <wordexp.h>
 
 #include <glib.h>
 
@@ -30,8 +32,11 @@
 #include "lib/bluetooth.h"
 #include "lib/uuid.h"
 
+#include "profiles/audio/a2dp-codecs.h"
+
 #include "src/shared/util.h"
 #include "src/shared/shell.h"
+#include "src/shared/io.h"
 #include "player.h"
 
 /* String display constants */
@@ -39,15 +44,50 @@
 #define COLORED_CHG	COLOR_YELLOW "CHG" COLOR_OFF
 #define COLORED_DEL	COLOR_RED "DEL" COLOR_OFF
 
+#define BLUEZ_MEDIA_INTERFACE "org.bluez.Media1"
 #define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
 #define BLUEZ_MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
 #define BLUEZ_MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
+#define BLUEZ_MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
+
+#define BLUEZ_MEDIA_ENDPOINT_PATH "/local/endpoint"
+
+#define NSEC_USEC(_t) (_t / 1000L)
+#define SEC_USEC(_t)  (_t  * 1000000L)
+#define TS_USEC(_ts)  (SEC_USEC((_ts)->tv_sec) + NSEC_USEC((_ts)->tv_nsec))
+
+struct endpoint {
+	char *path;
+	char *uuid;
+	uint8_t codec;
+	struct iovec *caps;
+	bool auto_accept;
+	char *transport;
+};
 
 static DBusConnection *dbus_conn;
 static GDBusProxy *default_player;
+static GList *medias = NULL;
 static GList *players = NULL;
 static GList *folders = NULL;
 static GList *items = NULL;
+static GList *endpoints = NULL;
+static GList *local_endpoints = NULL;
+
+static void endpoint_unregister(void *data)
+{
+	struct endpoint *ep = data;
+
+	bt_shell_printf("Endpoint %s unregistered\n", ep->path);
+	g_dbus_unregister_interface(dbus_conn, ep->path,
+						BLUEZ_MEDIA_ENDPOINT_INTERFACE);
+}
+
+static void disconnect_handler(DBusConnection *connection, void *user_data)
+{
+	g_list_free_full(local_endpoints, endpoint_unregister);
+	local_endpoints = NULL;
+}
 
 static bool check_default_player(void)
 {
@@ -319,11 +359,10 @@ static void generic_callback(const DBusError *error, void *user_data)
 	if (dbus_error_is_set(error)) {
 		bt_shell_printf("Failed to set %s: %s\n", str, error->name);
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	} else {
+		bt_shell_printf("Changing %s succeeded\n", str);
+		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 	}
-
-	bt_shell_printf("Changing %s succeeded\n", str);
-
-	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
 static void cmd_equalizer(int argc, char *argv[])
@@ -450,6 +489,17 @@ static char *proxy_description(GDBusProxy *proxy, const char *title,
 					title, path);
 }
 
+static void print_media(GDBusProxy *proxy, const char *description)
+{
+	char *str;
+
+	str = proxy_description(proxy, "Media", description);
+
+	bt_shell_printf("%s\n", str);
+
+	g_free(str);
+}
+
 static void print_player(GDBusProxy *proxy, const char *description)
 {
 	char *str;
@@ -948,6 +998,893 @@ static const struct bt_shell_menu player_menu = {
 	{} },
 };
 
+static char *endpoint_generator(const char *text, int state)
+{
+	return generic_generator(text, state, endpoints);
+}
+
+static char *local_endpoint_generator(const char *text, int state)
+{
+	return generic_generator(text, state, local_endpoints);
+}
+
+static void print_endpoint(void *data, void *user_data)
+{
+	GDBusProxy *proxy = data;
+	const char *description = user_data;
+	char *str;
+
+	str = proxy_description(proxy, "Endpoint", description);
+
+	bt_shell_printf("%s\n", str);
+
+	g_free(str);
+}
+
+static void cmd_list_endpoints(int argc, char *argv[])
+{
+	GList *l;
+
+	if (argc > 1) {
+		if (strcmp("local", argv[1])) {
+			bt_shell_printf("Endpoint list %s not available\n",
+					argv[1]);
+			return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+		}
+
+		for (l = local_endpoints; l; l = g_list_next(l)) {
+			struct endpoint *ep = l->data;
+
+			bt_shell_printf("Endpoint %s\n", ep->path);
+		}
+
+		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+	}
+
+	for (l = endpoints; l; l = g_list_next(l)) {
+		GDBusProxy *proxy = l->data;
+		print_endpoint(proxy, NULL);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void confirm_response(const char *input, void *user_data)
+{
+	DBusMessage *msg = user_data;
+
+	if (!strcasecmp(input, "y") || !strcasecmp(input, "yes"))
+		g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID);
+	else if (!strcasecmp(input, "n") || !strcmp(input, "no"))
+		g_dbus_send_error(dbus_conn, msg, "org.bluez.Error.Rejected",
+									NULL);
+	else
+		g_dbus_send_error(dbus_conn, msg, "org.bluez.Error.Canceled",
+									NULL);
+}
+
+static DBusMessage *endpoint_set_configuration(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct endpoint *ep = user_data;
+	DBusMessageIter args, props;
+	const char *path;
+
+	dbus_message_iter_init(msg, &args);
+
+	dbus_message_iter_get_basic(&args, &path);
+	dbus_message_iter_next(&args);
+
+	dbus_message_iter_recurse(&args, &props);
+	if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+		return g_dbus_create_error(msg,
+					 "org.bluez.Error.InvalidArguments",
+					 NULL);
+
+	bt_shell_printf("Endpoint: SetConfiguration\n");
+	bt_shell_printf("\tTransport %s\n", path);
+	print_iter("\t", "Properties", &props);
+
+	free(ep->transport);
+	ep->transport = strdup(path);
+
+	if (ep->auto_accept) {
+		bt_shell_printf("Auto Accepting...\n");
+		return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+	}
+
+	bt_shell_prompt_input("Endpoint", "Accept (yes/no):", confirm_response,
+							dbus_message_ref(msg));
+
+	return NULL;
+}
+
+static DBusMessage *endpoint_clear_configuration(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct endpoint *ep = user_data;
+
+	free(ep->transport);
+	ep->transport = NULL;
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static struct endpoint *endpoint_find(const char *pattern)
+{
+	GList *l;
+
+	for (l = local_endpoints; l; l = g_list_next(l)) {
+		struct endpoint *ep = l->data;
+
+		/* match object path */
+		if (!strcmp(ep->path, pattern))
+			return ep;
+
+		/* match UUID */
+		if (!strcmp(ep->uuid, pattern))
+			return ep;
+	}
+
+	return NULL;
+}
+
+static void cmd_show_endpoint(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	proxy = g_dbus_proxy_lookup(endpoints, NULL, argv[1],
+						BLUEZ_MEDIA_ENDPOINT_INTERFACE);
+	if (!proxy) {
+		bt_shell_printf("Endpoint %s not found\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+	}
+
+	bt_shell_printf("Endpoint %s\n", g_dbus_proxy_get_path(proxy));
+
+	print_property(proxy, "UUID");
+	print_property(proxy, "Codec");
+	print_property(proxy, "Capabilities");
+	print_property(proxy, "Device");
+	print_property(proxy, "DelayReporting");
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static const GDBusMethodTable endpoint_methods[] = {
+	{ GDBUS_ASYNC_METHOD("SetConfiguration",
+					GDBUS_ARGS({ "endpoint", "o" },
+						{ "properties", "a{sv}" } ),
+					NULL, endpoint_set_configuration) },
+	{ GDBUS_ASYNC_METHOD("ClearConfiguration",
+					GDBUS_ARGS({ "transport", "o" } ),
+					NULL, endpoint_clear_configuration) },
+	{ },
+};
+
+static void endpoint_free(void *data)
+{
+	struct endpoint *ep = data;
+
+	if (ep->caps) {
+		g_free(ep->caps->iov_base);
+		g_free(ep->caps);
+	}
+
+	g_free(ep->path);
+	g_free(ep->uuid);
+	g_free(ep);
+}
+
+static gboolean endpoint_get_uuid(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct endpoint *ep = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ep->uuid);
+
+	return TRUE;
+}
+
+static gboolean endpoint_get_codec(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct endpoint *ep = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &ep->codec);
+
+	return TRUE;
+}
+
+static gboolean endpoint_get_capabilities(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct endpoint *ep = 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,
+					     &ep->caps->iov_base,
+					     ep->caps->iov_len);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable endpoint_properties[] = {
+	{ "UUID", "s", endpoint_get_uuid, NULL, NULL },
+	{ "Codec", "y", endpoint_get_codec, NULL, NULL },
+	{ "Capabilities", "ay", endpoint_get_capabilities, NULL, NULL },
+	{ }
+};
+
+static uint8_t *str2bytearray(char *arg, size_t *val_len)
+{
+	uint8_t value[UINT8_MAX];
+	char *entry;
+	unsigned int i;
+
+	for (i = 0; (entry = strsep(&arg, " \t")) != NULL; i++) {
+		long val;
+		char *endptr = NULL;
+
+		if (*entry == '\0')
+			continue;
+
+		if (i >= G_N_ELEMENTS(value)) {
+			bt_shell_printf("Too much data\n");
+			return NULL;
+		}
+
+		val = strtol(entry, &endptr, 0);
+		if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
+			bt_shell_printf("Invalid value at index %d\n", i);
+			return NULL;
+		}
+
+		value[i] = val;
+	}
+
+	*val_len = i;
+
+	return g_memdup(value, i);
+}
+
+static void register_endpoint_setup(DBusMessageIter *iter, void *user_data)
+{
+	struct endpoint *ep = user_data;
+	DBusMessageIter dict;
+	const char *key = "Capabilities";
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &ep->path);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
+
+	g_dbus_dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &ep->uuid);
+
+	g_dbus_dict_append_entry(&dict, "Codec", DBUS_TYPE_BYTE, &ep->codec);
+
+	g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &key,
+					DBUS_TYPE_BYTE, &ep->caps->iov_base,
+					ep->caps->iov_len);
+
+	bt_shell_printf("Capabilities:\n");
+	bt_shell_hexdump(ep->caps->iov_base, ep->caps->iov_len);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void register_endpoint_reply(DBusMessage *message, void *user_data)
+{
+	struct endpoint *ep = user_data;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message)) {
+		bt_shell_printf("Failed to register endpoint: %s\n",
+				error.name);
+		dbus_error_free(&error);
+		local_endpoints = g_list_remove(local_endpoints, ep);
+		g_dbus_unregister_interface(dbus_conn, ep->path,
+						BLUEZ_MEDIA_ENDPOINT_INTERFACE);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	bt_shell_printf("Endpoint %s registered\n", ep->path);
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void endpoint_register(struct endpoint *ep)
+{
+	GList *l;
+
+	for (l = medias; l; l = g_list_next(l)) {
+		if (!g_dbus_proxy_method_call(l->data, "RegisterEndpoint",
+						register_endpoint_setup,
+						register_endpoint_reply,
+						ep, NULL)) {
+			bt_shell_printf("Failed register endpoint\n");
+			local_endpoints = g_list_remove(local_endpoints, ep);
+			g_dbus_unregister_interface(dbus_conn, ep->path,
+						BLUEZ_MEDIA_ENDPOINT_INTERFACE);
+			return bt_shell_noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+}
+
+static void endpoint_auto_accept(const char *input, void *user_data)
+{
+	struct endpoint *ep = user_data;
+
+	if (!strcasecmp(input, "y") || !strcasecmp(input, "yes"))
+		ep->auto_accept = true;
+	else if (!strcasecmp(input, "n") || !strcasecmp(input, "no"))
+		ep->auto_accept = false;
+	else
+		bt_shell_printf("Invalid input for Auto Accept\n");
+
+	endpoint_register(ep);
+}
+
+static void endpoint_set_capabilities(const char *input, void *user_data)
+{
+	struct endpoint *ep = user_data;
+
+	if (ep->caps)
+		g_free(ep->caps->iov_base);
+	else
+		ep->caps = g_new0(struct iovec, 1);
+
+	ep->caps->iov_base = str2bytearray((char *) input, &ep->caps->iov_len);
+
+	bt_shell_prompt_input(ep->path, "Auto Accept (yes/no):",
+						endpoint_auto_accept, ep);
+}
+
+struct codec_capabilities {
+	uint8_t len;
+	uint8_t type;
+	uint8_t data[UINT8_MAX];
+};
+
+#define data(args...) ((const unsigned char[]) { args })
+
+#define SBC_DATA(args...) \
+	{ \
+		.iov_base = (void *)data(args), \
+		.iov_len = sizeof(data(args)), \
+	}
+
+#define CODEC_CAPABILITIES(_uuid, _codec_id, _data) \
+	{ \
+		.uuid = _uuid, \
+		.codec_id = _codec_id, \
+		.data = _data, \
+	}
+
+static const struct capabilities {
+	const char *uuid;
+	uint8_t codec_id;
+	struct iovec data;
+} caps[] = {
+	/* A2DP SBC Source:
+	 *
+	 * Channel Modes: Mono DualChannel Stereo JointStereo
+	 * Frequencies: 16Khz 32Khz 44.1Khz 48Khz
+	 * Subbands: 4 8
+	 * Blocks: 4 8 12 16
+	 * Bitpool Range: 2-64
+	 */
+	CODEC_CAPABILITIES(A2DP_SOURCE_UUID, A2DP_CODEC_SBC,
+					SBC_DATA(0xff, 0xff, 2, 64)),
+	/* A2DP SBC Sink:
+	 *
+	 * Channel Modes: Mono DualChannel Stereo JointStereo
+	 * Frequencies: 16Khz 32Khz 44.1Khz 48Khz
+	 * Subbands: 4 8
+	 * Blocks: 4 8 12 16
+	 * Bitpool Range: 2-64
+	 */
+	CODEC_CAPABILITIES(A2DP_SINK_UUID, A2DP_CODEC_SBC,
+					SBC_DATA(0xff, 0xff, 2, 64)),
+};
+
+static char *uuid_generator(const char *text, int state)
+{
+	int len = strlen(text);
+	static int index = 0;
+	size_t i;
+
+	if (!state)
+		index = 0;
+
+	for (i = index; i < ARRAY_SIZE(caps); i++) {
+		const struct capabilities *cap = &caps[i];
+
+		index++;
+
+		if (!strncasecmp(cap->uuid, text, len))
+			return strdup(cap->uuid);
+	}
+
+	return NULL;
+}
+
+static const struct capabilities *find_capabilities(const char *uuid,
+							uint8_t codec_id)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(caps); i++) {
+		const struct capabilities *cap = &caps[i];
+
+		if (strcasecmp(cap->uuid, uuid))
+			continue;
+
+		if (cap->codec_id == codec_id)
+			return cap;
+	}
+
+	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;
+	char *endptr = NULL;
+
+	ep = g_new0(struct endpoint, 1);
+	ep->uuid = g_strdup(argv[1]);
+	ep->codec = strtol(argv[2], &endptr, 0);
+	ep->path = g_strdup_printf("%s/ep%u", BLUEZ_MEDIA_ENDPOINT_PATH,
+					g_list_length(local_endpoints));
+	local_endpoints = g_list_append(local_endpoints, ep);
+
+	if (!g_dbus_register_interface(dbus_conn, ep->path,
+					BLUEZ_MEDIA_ENDPOINT_INTERFACE,
+					endpoint_methods, NULL,
+					endpoint_properties, ep,
+					endpoint_free)) {
+		bt_shell_printf("Failed to register endpoint object\n");
+		local_endpoints = g_list_remove(local_endpoints, ep);
+		endpoint_free(ep);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (argc > 3)
+		endpoint_set_capabilities(argv[3], ep);
+	else {
+		const struct capabilities *cap;
+
+		cap = find_capabilities(ep->uuid, ep->codec);
+		if (cap) {
+			if (ep->caps)
+				ep->caps->iov_len = 0;
+
+			/* Copy capabilities */
+			iov_append(&ep->caps, cap->data.iov_base,
+							cap->data.iov_len);
+
+			bt_shell_prompt_input(ep->path, "Auto Accept (yes/no):",
+						endpoint_auto_accept, ep);
+		} else
+			bt_shell_prompt_input(ep->path, "Enter capabilities:",
+						endpoint_set_capabilities, ep);
+	}
+}
+
+static void unregister_endpoint_setup(DBusMessageIter *iter, void *user_data)
+{
+	struct endpoint *ep = user_data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &ep->path);
+}
+
+static void unregister_endpoint_reply(DBusMessage *message, void *user_data)
+{
+	struct endpoint *ep = user_data;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message)) {
+		bt_shell_printf("Failed to unregister endpoint: %s\n",
+				error.name);
+		dbus_error_free(&error);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	bt_shell_printf("Endpoint %s unregistered\n", ep->path);
+
+	local_endpoints = g_list_remove(local_endpoints, ep);
+	g_dbus_unregister_interface(dbus_conn, ep->path,
+					BLUEZ_MEDIA_ENDPOINT_INTERFACE);
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_unregister_endpoint(int argc, char *argv[])
+{
+	struct endpoint *ep;
+	GList *l;
+
+	ep = endpoint_find(argv[1]);
+	if (!ep) {
+		bt_shell_printf("Failed to unregister endpoint object\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	for (l = medias; l; l = g_list_next(l)) {
+		if (!g_dbus_proxy_method_call(l->data, "UnregisterEndpoint",
+						unregister_endpoint_setup,
+						unregister_endpoint_reply,
+						ep, NULL)) {
+			bt_shell_printf("Failed unregister endpoint\n");
+			return bt_shell_noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+struct codec_qos {
+	uint32_t interval;
+	uint8_t  framing;
+	char *phy;
+	uint16_t sdu;
+	uint8_t  rtn;
+	uint16_t latency;
+	uint32_t delay;
+};
+
+struct endpoint_config {
+	GDBusProxy *proxy;
+	struct endpoint *ep;
+	struct iovec *caps;
+	struct codec_qos qos;
+};
+
+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);
+
+	bt_shell_printf("Interval %u\n", cfg->qos.interval);
+
+	g_dbus_dict_append_entry(&dict, "Interval", DBUS_TYPE_UINT32,
+						&cfg->qos.interval);
+
+	bt_shell_printf("Framing %s\n", cfg->qos.framing ? "true" : "false");
+
+	g_dbus_dict_append_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN,
+						&cfg->qos.framing);
+
+	bt_shell_printf("PHY %s\n", cfg->qos.phy);
+
+	g_dbus_dict_append_entry(&dict, "PHY", DBUS_TYPE_STRING, &cfg->qos.phy);
+
+	bt_shell_printf("SDU %u\n", cfg->qos.sdu);
+
+	g_dbus_dict_append_entry(&dict, "SDU", DBUS_TYPE_UINT16,
+						&cfg->qos.sdu);
+
+	bt_shell_printf("Retransmissions %u\n", cfg->qos.rtn);
+
+	g_dbus_dict_append_entry(&dict, "Retransmissions", DBUS_TYPE_BYTE,
+						&cfg->qos.rtn);
+
+	bt_shell_printf("Latency %u\n", cfg->qos.latency);
+
+	g_dbus_dict_append_entry(&dict, "Latency", DBUS_TYPE_UINT16,
+						&cfg->qos.latency);
+
+	bt_shell_printf("Delay %u\n", cfg->qos.delay);
+
+	g_dbus_dict_append_entry(&dict, "Delay", DBUS_TYPE_UINT32,
+						&cfg->qos.delay);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void config_endpoint_reply(DBusMessage *message, void *user_data)
+{
+	struct endpoint_config *cfg = user_data;
+	struct endpoint *ep = cfg->ep;
+	DBusError error;
+
+	free(cfg->caps->iov_base);
+	free(cfg->caps);
+	free(cfg);
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message)) {
+		bt_shell_printf("Failed to config endpoint: %s\n",
+				error.name);
+		dbus_error_free(&error);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	bt_shell_printf("Endpoint %s configured\n", ep->path);
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void endpoint_set_config(struct endpoint_config *cfg)
+{
+	if (!g_dbus_proxy_method_call(cfg->proxy, "SetConfiguration",
+						config_endpoint_setup,
+						config_endpoint_reply,
+						cfg, NULL)) {
+		bt_shell_printf("Failed to config endpoint\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void qos_delay(const char *input, void *user_data)
+{
+	struct endpoint_config *cfg = user_data;
+
+	cfg->qos.delay = strtol(input, NULL, 0);
+
+	endpoint_set_config(cfg);
+}
+
+static void qos_latency(const char *input, void *user_data)
+{
+	struct endpoint_config *cfg = user_data;
+
+	cfg->qos.latency = strtol(input, NULL, 0);
+
+	bt_shell_prompt_input(cfg->ep->path, "Enter Delay:", qos_delay, cfg);
+}
+
+static void qos_rtn(const char *input, void *user_data)
+{
+	struct endpoint_config *cfg = user_data;
+
+	cfg->qos.rtn = strtol(input, NULL, 0);
+
+	bt_shell_prompt_input(cfg->ep->path, "Enter Latency:",
+							qos_latency, cfg);
+}
+
+static void qos_sdu(const char *input, void *user_data)
+{
+	struct endpoint_config *cfg = user_data;
+
+	cfg->qos.sdu = strtol(input, NULL, 0);
+
+	bt_shell_prompt_input(cfg->ep->path, "Enter Retransmissions:",
+							qos_rtn, cfg);
+}
+
+static void qos_phy(const char *input, void *user_data)
+{
+	struct endpoint_config *cfg = user_data;
+
+	cfg->qos.phy = strdup(input);
+
+	bt_shell_prompt_input(cfg->ep->path, "Enter SDU:", qos_sdu, cfg);
+}
+
+static void qos_framing(const char *input, void *user_data)
+{
+	struct endpoint_config *cfg = user_data;
+
+	cfg->qos.framing = strtol(input, NULL, 0);
+
+	bt_shell_prompt_input(cfg->ep->path, "Enter PHY:", qos_phy, cfg);
+}
+
+static void qos_interval(const char *input, void *user_data)
+{
+	struct endpoint_config *cfg = user_data;
+
+	cfg->qos.interval = strtol(input, NULL, 0);
+
+	bt_shell_prompt_input(cfg->ep->path, "Enter Framing:", qos_framing,
+									cfg);
+}
+
+static void endpoint_config(const char *input, void *user_data)
+{
+	struct endpoint_config *cfg = user_data;
+	uint8_t *data;
+	size_t len;
+
+	data = str2bytearray((char *) input, &len);
+
+	iov_append(&cfg->caps, data, len);
+
+	bt_shell_prompt_input(cfg->ep->path, "Enter Interval:", qos_interval,
+									cfg);
+}
+
+struct codec_preset {
+	const char *name;
+	const struct iovec data;
+};
+
+#define SBC_PRESET(_name, _data) \
+	{ \
+		.name = _name, \
+		.data = _data, \
+	}
+
+static const struct codec_preset sbc_presets[] = {
+	/* Table 4.7: Recommended sets of SBC parameters in the SRC device
+	 * Other settings: Block length = 16, Allocation method = Loudness,
+	 * Subbands = 8.
+	 */
+	SBC_PRESET("MQ_MONO_44_1",
+		SBC_DATA(0x28, 0x15, 2, SBC_BITPOOL_MQ_MONO_44100)),
+	SBC_PRESET("MQ_MONO_48_1",
+		SBC_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)),
+	SBC_PRESET("MQ_STEREO_48_1",
+		SBC_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)),
+	SBC_PRESET("HQ_MONO_48_1",
+		SBC_DATA(0x18, 0x15, 2, SBC_BITPOOL_HQ_MONO_48000)),
+	SBC_PRESET("HQ_STEREO_44_1",
+		SBC_DATA(0x21, 0x15, 2, SBC_BITPOOL_HQ_JOINT_STEREO_44100)),
+	SBC_PRESET("HQ_STEREO_48_1",
+		SBC_DATA(0x11, 0x15, 2, SBC_BITPOOL_HQ_JOINT_STEREO_48000)),
+};
+
+#define PRESET(_uuid, _presets) \
+	{ \
+		.uuid = _uuid, \
+		.presets = _presets, \
+		.num_presets = ARRAY_SIZE(_presets), \
+	}
+
+static const struct preset {
+	const char *uuid;
+	const struct codec_preset *presets;
+	size_t num_presets;
+} presets[] = {
+	PRESET(A2DP_SOURCE_UUID, sbc_presets),
+	PRESET(A2DP_SINK_UUID, sbc_presets),
+};
+
+static const struct codec_preset *find_preset(const char *uuid,
+						const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(presets); i++) {
+		const struct preset *preset = &presets[i];
+
+		if (!strcasecmp(preset->uuid, uuid)) {
+			size_t j;
+
+			for (j = 0; j < preset->num_presets; j++) {
+				const struct codec_preset *p;
+
+				p = &preset->presets[j];
+
+				if (!strcmp(p->name, name))
+					return p;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static void cmd_config_endpoint(int argc, char *argv[])
+{
+	struct endpoint_config *cfg;
+	const struct codec_preset *preset;
+
+	cfg = new0(struct endpoint_config, 1);
+
+	cfg->proxy = g_dbus_proxy_lookup(endpoints, NULL, argv[1],
+						BLUEZ_MEDIA_ENDPOINT_INTERFACE);
+	if (!cfg->proxy) {
+		bt_shell_printf("Endpoint %s not found\n", argv[1]);
+		goto fail;
+	}
+
+	cfg->ep = endpoint_find(argv[2]);
+	if (!cfg->ep) {
+		bt_shell_printf("Local Endpoint %s not found\n", argv[2]);
+		goto fail;
+	}
+
+	if (argc > 3) {
+		preset = find_preset(cfg->ep->uuid, argv[3]);
+		if (!preset) {
+			bt_shell_printf("Preset %s not found\n", argv[3]);
+			goto fail;
+		}
+
+		/* Copy capabilities */
+		iov_append(&cfg->caps, preset->data.iov_base,
+						preset->data.iov_len);
+
+		endpoint_set_config(cfg);
+		return;
+	}
+
+	bt_shell_prompt_input(cfg->ep->path, "Enter configuration:",
+					endpoint_config, cfg);
+
+	return;
+
+fail:
+	g_free(cfg);
+	return bt_shell_noninteractive_quit(EXIT_FAILURE);
+}
+
+static const struct bt_shell_menu endpoint_menu = {
+	.name = "endpoint",
+	.desc = "Media Endpoint Submenu",
+	.entries = {
+	{ "list",         "[local]",    cmd_list_endpoints,
+						"List available endpoints" },
+	{ "show",         "<endpoint>", cmd_show_endpoint,
+						"Endpoint information",
+						endpoint_generator },
+	{ "register",     "<UUID> <codec> [capabilities...]",
+						cmd_register_endpoint,
+						"Register Endpoint",
+						uuid_generator },
+	{ "unregister",   "<UUID/object>", cmd_unregister_endpoint,
+						"Register Endpoint",
+						local_endpoint_generator },
+	{ "config",       "<endpoint> <local endpoint> [preset]",
+						cmd_config_endpoint,
+						"Configure Endpoint",
+						endpoint_generator },
+	{} },
+};
+
+static void media_added(GDBusProxy *proxy)
+{
+	medias = g_list_append(medias, proxy);
+
+	print_media(proxy, COLORED_NEW);
+}
+
 static void player_added(GDBusProxy *proxy)
 {
 	players = g_list_append(players, proxy);
@@ -1002,18 +1939,36 @@ static void item_added(GDBusProxy *proxy)
 	print_item(proxy, COLORED_NEW);
 }
 
+static void endpoint_added(GDBusProxy *proxy)
+{
+	endpoints = g_list_append(endpoints, proxy);
+
+	print_endpoint(proxy, COLORED_NEW);
+}
+
 static void proxy_added(GDBusProxy *proxy, void *user_data)
 {
 	const char *interface;
 
 	interface = g_dbus_proxy_get_interface(proxy);
 
-	if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE))
+	if (!strcmp(interface, BLUEZ_MEDIA_INTERFACE))
+		media_added(proxy);
+	else if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE))
 		player_added(proxy);
 	else if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE))
 		folder_added(proxy);
 	else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE))
 		item_added(proxy);
+	else if (!strcmp(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE))
+		endpoint_added(proxy);
+}
+
+static void media_removed(GDBusProxy *proxy)
+{
+	print_media(proxy, COLORED_DEL);
+
+	medias = g_list_remove(medias, proxy);
 }
 
 static void player_removed(GDBusProxy *proxy)
@@ -1040,18 +1995,29 @@ static void item_removed(GDBusProxy *proxy)
 	print_item(proxy, COLORED_DEL);
 }
 
+static void endpoint_removed(GDBusProxy *proxy)
+{
+	endpoints = g_list_remove(endpoints, proxy);
+
+	print_endpoint(proxy, COLORED_DEL);
+}
+
 static void proxy_removed(GDBusProxy *proxy, void *user_data)
 {
 	const char *interface;
 
 	interface = g_dbus_proxy_get_interface(proxy);
 
+	if (!strcmp(interface, BLUEZ_MEDIA_INTERFACE))
+		media_removed(proxy);
 	if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE))
 		player_removed(proxy);
 	if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE))
 		folder_removed(proxy);
 	if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE))
 		item_removed(proxy);
+	if (!strcmp(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE))
+		endpoint_removed(proxy);
 }
 
 static void player_property_changed(GDBusProxy *proxy, const char *name,
@@ -1084,6 +2050,16 @@ static void item_property_changed(GDBusProxy *proxy, const char *name,
 	g_free(str);
 }
 
+static void endpoint_property_changed(GDBusProxy *proxy, const char *name,
+						DBusMessageIter *iter)
+{
+	char *str;
+
+	str = proxy_description(proxy, "Endpoint", COLORED_CHG);
+	print_iter(str, name, iter);
+	g_free(str);
+}
+
 static void property_changed(GDBusProxy *proxy, const char *name,
 					DBusMessageIter *iter, void *user_data)
 {
@@ -1097,6 +2073,8 @@ static void property_changed(GDBusProxy *proxy, const char *name,
 		folder_property_changed(proxy, name, iter);
 	else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE))
 		item_property_changed(proxy, name, iter);
+	else if (!strcmp(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE))
+		endpoint_property_changed(proxy, name, iter);
 }
 
 static GDBusClient *client;
@@ -1104,6 +2082,7 @@ static GDBusClient *client;
 void player_add_submenu(void)
 {
 	bt_shell_add_submenu(&player_menu);
+	bt_shell_add_submenu(&endpoint_menu);
 
 	dbus_conn = bt_shell_get_env("DBUS_CONNECTION");
 	if (!dbus_conn || client)
@@ -1113,6 +2092,7 @@ void player_add_submenu(void)
 
 	g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
 							property_changed, NULL);
+	g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
 }
 
 void player_remove_submenu(void)
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH BlueZ 2/2] client/player: Add transport menu
  2022-04-11 23:58 [PATCH BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete Luiz Augusto von Dentz
  2022-04-11 23:58 ` [PATCH BlueZ 1/2] client/player: Add endpoint menu Luiz Augusto von Dentz
@ 2022-04-11 23:58 ` Luiz Augusto von Dentz
  2022-04-12  1:56 ` [BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete bluez.test.bot
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2022-04-11 23:58 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds transport menu:

[bluetooth]# menu transport
Menu transport:
Available commands:
-------------------
list                                              List available transports
show <transport>                                  Transport information
acquire <transport>                               Acquire Transport
release <transport>                               Release Transport
send <filename>                                   Send contents of a file
---
 client/player.c | 385 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 384 insertions(+), 1 deletion(-)

diff --git a/client/player.c b/client/player.c
index b0a8b2c56..9212f31ac 100644
--- a/client/player.c
+++ b/client/player.c
@@ -49,6 +49,7 @@
 #define BLUEZ_MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
 #define BLUEZ_MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
 #define BLUEZ_MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
+#define BLUEZ_MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1"
 
 #define BLUEZ_MEDIA_ENDPOINT_PATH "/local/endpoint"
 
@@ -73,6 +74,16 @@ static GList *folders = NULL;
 static GList *items = NULL;
 static GList *endpoints = NULL;
 static GList *local_endpoints = NULL;
+static GList *transports = NULL;
+
+struct transport {
+	int sk;
+	int mtu[2];
+	struct io *io;
+	uint32_t seq;
+} transport = {
+	.sk = -1,
+};
 
 static void endpoint_unregister(void *data)
 {
@@ -1400,8 +1411,9 @@ static char *uuid_generator(const char *text, int state)
 	static int index = 0;
 	size_t i;
 
-	if (!state)
+	if (!state) {
 		index = 0;
+	}
 
 	for (i = index; i < ARRAY_SIZE(caps); i++) {
 		const struct capabilities *cap = &caps[i];
@@ -1946,6 +1958,26 @@ static void endpoint_added(GDBusProxy *proxy)
 	print_endpoint(proxy, COLORED_NEW);
 }
 
+static void print_transport(void *data, void *user_data)
+{
+	GDBusProxy *proxy = data;
+	const char *description = user_data;
+	char *str;
+
+	str = proxy_description(proxy, "Transport", description);
+
+	bt_shell_printf("%s\n", str);
+
+	g_free(str);
+}
+
+static void transport_added(GDBusProxy *proxy)
+{
+	transports = g_list_append(transports, proxy);
+
+	print_transport(proxy, COLORED_NEW);
+}
+
 static void proxy_added(GDBusProxy *proxy, void *user_data)
 {
 	const char *interface;
@@ -1962,6 +1994,8 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
 		item_added(proxy);
 	else if (!strcmp(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE))
 		endpoint_added(proxy);
+	else if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE))
+		transport_added(proxy);
 }
 
 static void media_removed(GDBusProxy *proxy)
@@ -2002,6 +2036,13 @@ static void endpoint_removed(GDBusProxy *proxy)
 	print_endpoint(proxy, COLORED_DEL);
 }
 
+static void transport_removed(GDBusProxy *proxy)
+{
+	transports = g_list_remove(transports, proxy);
+
+	print_transport(proxy, COLORED_DEL);
+}
+
 static void proxy_removed(GDBusProxy *proxy, void *user_data)
 {
 	const char *interface;
@@ -2018,6 +2059,8 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
 		item_removed(proxy);
 	if (!strcmp(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE))
 		endpoint_removed(proxy);
+	if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE))
+		transport_removed(proxy);
 }
 
 static void player_property_changed(GDBusProxy *proxy, const char *name,
@@ -2060,6 +2103,134 @@ static void endpoint_property_changed(GDBusProxy *proxy, const char *name,
 	g_free(str);
 }
 
+static struct endpoint *find_ep_by_transport(const char *path)
+{
+	GList *l;
+
+	for (l = local_endpoints; l; l = g_list_next(l)) {
+		struct endpoint *ep = l->data;
+
+		if (ep->transport && !strcmp(ep->transport, path))
+			return ep;
+	}
+
+	return NULL;
+}
+
+static bool transport_disconnected(struct io *io, void *user_data)
+{
+	bt_shell_printf("Transport fd disconnected\n");
+
+	io_destroy(transport.io);
+	transport.io = NULL;
+	transport.sk = -1;
+
+	return false;
+}
+
+static bool transport_recv(struct io *io, void *user_data)
+{
+	uint8_t buf[1024];
+	int ret;
+
+	ret = read(io_get_fd(io), buf, sizeof(buf));
+	if (ret < 0) {
+		bt_shell_printf("Failed to read: %s (%d)\n", strerror(errno),
+								-errno);
+		return true;
+	}
+
+	bt_shell_printf("[seq %d] recv: %u bytes\n", transport.seq, ret);
+
+	transport.seq++;
+
+	return true;
+}
+
+static void acquire_reply(DBusMessage *message, void *user_data)
+{
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		bt_shell_printf("Failed to acquire: %s\n", error.name);
+		dbus_error_free(&error);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (!dbus_message_get_args(message, &error,
+				   DBUS_TYPE_UNIX_FD, &transport.sk,
+				   DBUS_TYPE_UINT16, &transport.mtu[0],
+				   DBUS_TYPE_UINT16, &transport.mtu[1],
+				   DBUS_TYPE_INVALID)) {
+		bt_shell_printf("Failed to parse Acquire() reply: %s",
+							error.name);
+		dbus_error_free(&error);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	bt_shell_printf("Acquire successful: fd %d MTU %d:%d\n", transport.sk,
+					transport.mtu[0], transport.mtu[1]);
+
+	io_destroy(transport.io);
+	transport.io = io_new(transport.sk);
+
+	io_set_disconnect_handler(transport.io, transport_disconnected, NULL,
+								NULL);
+	io_set_read_handler(transport.io, transport_recv, NULL, NULL);
+
+	return bt_shell_noninteractive_quit(EXIT_FAILURE);
+}
+
+static void transport_acquire(const char *input, void *user_data)
+{
+	GDBusProxy *proxy = user_data;
+
+	if (!strcasecmp(input, "y") || !strcasecmp(input, "yes")) {
+		if (!g_dbus_proxy_method_call(proxy, "Acquire", NULL,
+						acquire_reply, NULL, NULL))
+			bt_shell_printf("Failed acquire transport\n");
+	}
+}
+
+static void transport_property_changed(GDBusProxy *proxy, const char *name,
+						DBusMessageIter *iter)
+{
+	char *str;
+	struct endpoint *ep;
+
+	str = proxy_description(proxy, "Transport", COLORED_CHG);
+	print_iter(str, name, iter);
+	g_free(str);
+
+	if (strcmp(name, "State"))
+		return;
+
+	dbus_message_iter_get_basic(iter, &str);
+
+	if (strcmp(str, "pending"))
+		return;
+
+	/* Only attempt to acquire if transport is configured with a local
+	 * endpoint.
+	 */
+	ep = find_ep_by_transport(g_dbus_proxy_get_path(proxy));
+	if (!ep)
+		return;
+
+	if (ep->auto_accept) {
+		bt_shell_printf("Auto Accepting...\n");
+		if (!g_dbus_proxy_method_call(proxy, "Acquire", NULL,
+						acquire_reply, NULL, NULL))
+			bt_shell_printf("Failed acquire transport\n");
+		return;
+	}
+
+	bt_shell_prompt_input(g_dbus_proxy_get_path(proxy), "Acquire (yes/no):",
+					transport_acquire, proxy);
+}
+
 static void property_changed(GDBusProxy *proxy, const char *name,
 					DBusMessageIter *iter, void *user_data)
 {
@@ -2075,14 +2246,226 @@ static void property_changed(GDBusProxy *proxy, const char *name,
 		item_property_changed(proxy, name, iter);
 	else if (!strcmp(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE))
 		endpoint_property_changed(proxy, name, iter);
+	else if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE))
+		transport_property_changed(proxy, name, iter);
 }
 
+static char *transport_generator(const char *text, int state)
+{
+	return generic_generator(text, state, transports);
+}
+
+static void cmd_list_transport(int argc, char *argv[])
+{
+	GList *l;
+
+	for (l = transports; l; l = g_list_next(l)) {
+		GDBusProxy *proxy = l->data;
+		print_transport(proxy, NULL);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_show_transport(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	proxy = g_dbus_proxy_lookup(transports, NULL, argv[1],
+					BLUEZ_MEDIA_TRANSPORT_INTERFACE);
+	if (!proxy) {
+		bt_shell_printf("Transport %s not found\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	bt_shell_printf("Transport %s\n", g_dbus_proxy_get_path(proxy));
+
+	print_property(proxy, "UUID");
+	print_property(proxy, "Codec");
+	print_property(proxy, "Configuration");
+	print_property(proxy, "Device");
+	print_property(proxy, "State");
+	print_property(proxy, "Delay");
+	print_property(proxy, "Volume");
+	print_property(proxy, "Endpoint");
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_acquire_transport(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	if (transport.sk >= 0) {
+		bt_shell_printf("Transport socked %d already acquired\n",
+							transport.sk);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	proxy = g_dbus_proxy_lookup(transports, NULL, argv[1],
+					BLUEZ_MEDIA_TRANSPORT_INTERFACE);
+	if (!proxy) {
+		bt_shell_printf("Transport %s not found\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (!g_dbus_proxy_method_call(proxy, "Acquire", NULL,
+					acquire_reply, NULL, NULL)) {
+		bt_shell_printf("Failed acquire transport\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void release_reply(DBusMessage *message, void *user_data)
+{
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		bt_shell_printf("Failed to release: %s\n", error.name);
+		dbus_error_free(&error);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	close(transport.sk);
+	transport.sk = -1;
+
+	bt_shell_printf("Release successful\n");
+
+	return bt_shell_noninteractive_quit(EXIT_FAILURE);
+}
+
+static void cmd_release_transport(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	if (transport.sk < 0) {
+		bt_shell_printf("No Transport Socked found\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	proxy = g_dbus_proxy_lookup(transports, NULL, argv[1],
+					BLUEZ_MEDIA_TRANSPORT_INTERFACE);
+	if (!proxy) {
+		bt_shell_printf("Transport %s not found\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (!g_dbus_proxy_method_call(proxy, "Release", NULL,
+					release_reply, NULL, NULL)) {
+		bt_shell_printf("Failed release transport\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static int open_file(const char *filename)
+{
+	int fd = -1;
+
+	bt_shell_printf("Opening %s ...\n", filename);
+
+	fd = open(filename, O_RDONLY);
+	if (fd <= 0)
+		bt_shell_printf("Can't open file %s: %s\n", filename,
+						strerror(errno));
+
+	return fd;
+}
+
+static int transport_send(int fd)
+{
+	uint8_t *buf;
+
+	buf = malloc(transport.mtu[1]);
+	if (!buf) {
+		bt_shell_printf("malloc: %s (%d)", strerror(errno), errno);
+		return -ENOMEM;
+	}
+
+	for (transport.seq = 0; ; transport.seq++) {
+		ssize_t ret;
+		int queued;
+
+		ret = read(fd, buf, transport.mtu[1]);
+		if (ret <= 0) {
+			if (ret < 0)
+				bt_shell_printf("read failed: %s (%d)",
+						strerror(errno), errno);
+			close(fd);
+			return ret;
+		}
+
+		ret = send(transport.sk, buf, ret, 0);
+		if (ret <= 0) {
+			bt_shell_printf("Send failed: %s (%d)",
+							strerror(errno), errno);
+			return -errno;
+		}
+
+		ioctl(transport.sk, TIOCOUTQ, &queued);
+
+		bt_shell_printf("[seq %d] send: %zd bytes "
+				"(TIOCOUTQ %d bytes)\n",
+				transport.seq, ret, queued);
+	}
+
+	free(buf);
+}
+
+static void cmd_send_transport(int argc, char *argv[])
+{
+	int fd, err;
+
+	if (transport.sk < 0) {
+		bt_shell_printf("No Transport Socked found\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	fd = open_file(argv[1]);
+
+	bt_shell_printf("Sending ...\n");
+	err = transport_send(fd);
+
+	close(fd);
+
+	if (err < 0)
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static const struct bt_shell_menu transport_menu = {
+	.name = "transport",
+	.desc = "Media Transport Submenu",
+	.entries = {
+	{ "list",         NULL,    cmd_list_transport,
+						"List available transports" },
+	{ "show",        "<transport>", cmd_show_transport,
+						"Transport information",
+						transport_generator },
+	{ "acquire",     "<transport>",	cmd_acquire_transport,
+						"Acquire Transport",
+						transport_generator },
+	{ "release",     "<transport>",	cmd_release_transport,
+						"Release Transport",
+						transport_generator },
+	{ "send",        "<filename>",	cmd_send_transport,
+						"Send contents of a file" },
+	{} },
+};
+
 static GDBusClient *client;
 
 void player_add_submenu(void)
 {
 	bt_shell_add_submenu(&player_menu);
 	bt_shell_add_submenu(&endpoint_menu);
+	bt_shell_add_submenu(&transport_menu);
 
 	dbus_conn = bt_shell_get_env("DBUS_CONNECTION");
 	if (!dbus_conn || client)
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* RE: [BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete
  2022-04-11 23:58 [PATCH BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete Luiz Augusto von Dentz
  2022-04-11 23:58 ` [PATCH BlueZ 1/2] client/player: Add endpoint menu Luiz Augusto von Dentz
  2022-04-11 23:58 ` [PATCH BlueZ 2/2] client/player: Add transport menu Luiz Augusto von Dentz
@ 2022-04-12  1:56 ` bluez.test.bot
  2022-04-12  5:28 ` [PATCH BlueZ] " Paul Menzel
  2022-04-15  0:10 ` patchwork-bot+bluetooth
  4 siblings, 0 replies; 7+ messages in thread
From: bluez.test.bot @ 2022-04-12  1:56 UTC (permalink / raw)
  To: linux-bluetooth, luiz.dentz

[-- Attachment #1: Type: text/plain, Size: 531 bytes --]

This is an automated email and please do not reply to this email.

Dear Submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository.

----- Output -----
error: patch failed: client/player.c:49
error: client/player.c: patch does not apply
hint: Use 'git am --show-current-patch' to see the failed patch


Please resolve the issue and submit the patches again.


---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 7+ messages in thread

* RE: [BlueZ,1/2] client/player: Add endpoint menu
  2022-04-11 23:58 ` [PATCH BlueZ 1/2] client/player: Add endpoint menu Luiz Augusto von Dentz
@ 2022-04-12  2:52   ` bluez.test.bot
  0 siblings, 0 replies; 7+ messages in thread
From: bluez.test.bot @ 2022-04-12  2:52 UTC (permalink / raw)
  To: linux-bluetooth, luiz.dentz

[-- Attachment #1: Type: text/plain, Size: 2706 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=631261

---Test result---

Test Summary:
CheckPatch                    FAIL      2.59 seconds
GitLint                       PASS      1.04 seconds
Prep - Setup ELL              PASS      43.20 seconds
Build - Prep                  PASS      0.72 seconds
Build - Configure             PASS      8.65 seconds
Build - Make                  PASS      1291.87 seconds
Make Check                    PASS      11.53 seconds
Make Check w/Valgrind         PASS      448.81 seconds
Make Distcheck                PASS      231.60 seconds
Build w/ext ELL - Configure   PASS      8.76 seconds
Build w/ext ELL - Make        PASS      1265.50 seconds
Incremental Build with patchesPASS      0.00 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script with rule in .checkpatch.conf
Output:
[BlueZ,1/2] client/player: Add endpoint menu
ERROR:INITIALISED_STATIC: do not initialise statics to NULL
#150: FILE: client/player.c:70:
+static GList *medias = NULL;

ERROR:INITIALISED_STATIC: do not initialise statics to NULL
#154: FILE: client/player.c:74:
+static GList *endpoints = NULL;

ERROR:INITIALISED_STATIC: do not initialise statics to NULL
#155: FILE: client/player.c:75:
+static GList *local_endpoints = NULL;

WARNING:LINE_SPACING: Missing a blank line after declarations
#256: FILE: client/player.c:1046:
+		GDBusProxy *proxy = l->data;
+		print_endpoint(proxy, NULL);

ERROR:SPACING: space prohibited before that close parenthesis ')'
#367: FILE: client/player.c:1157:
+						{ "properties", "a{sv}" } ),

ERROR:SPACING: space prohibited before that close parenthesis ')'
#370: FILE: client/player.c:1160:
+					GDBUS_ARGS({ "transport", "o" } ),

ERROR:INITIALISED_STATIC: do not initialise statics to 0
#610: FILE: client/player.c:1400:
+	static int index = 0;

/github/workspace/src/12809906.patch total: 6 errors, 1 warnings, 1098 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/12809906.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.




---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete
  2022-04-11 23:58 [PATCH BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete Luiz Augusto von Dentz
                   ` (2 preceding siblings ...)
  2022-04-12  1:56 ` [BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete bluez.test.bot
@ 2022-04-12  5:28 ` Paul Menzel
  2022-04-15  0:10 ` patchwork-bot+bluetooth
  4 siblings, 0 replies; 7+ messages in thread
From: Paul Menzel @ 2022-04-12  5:28 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Dear Luiz,


Thank you for your patch.

Am 12.04.22 um 01:58 schrieb Luiz Augusto von Dentz:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> 
> This fixes not clenup ssp_status and ssp_auto_complete flags on

cle*a*nup

> auth_complete.

I’d just write:

Clean up ssp_status and ssp_auto_complete flags on auth_complete

(Same for the commit message summary.)

> ---
>   emulator/btdev.c | 5 +++++
>   1 file changed, 5 insertions(+)
> 
> diff --git a/emulator/btdev.c b/emulator/btdev.c
> index f70fa9136..ccfaed36b 100644
> --- a/emulator/btdev.c
> +++ b/emulator/btdev.c
> @@ -1366,6 +1366,11 @@ static void auth_complete(struct btdev_conn *conn, uint8_t status)
>   	ev.status = status;
>   
>   	send_event(conn->dev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
> +
> +	conn->dev->ssp_status = 0;
> +	conn->dev->ssp_auth_complete = false;
> +	conn->link->dev->ssp_status = 0;
> +	conn->link->dev->ssp_auth_complete = false;
>   }
>   
>   static int cmd_link_key_reply_complete(struct btdev *dev, const void *data,

Acked-by: Paul Menzel <pmenzel@molgen.mpg.de>


Kind regards,

Paul

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete
  2022-04-11 23:58 [PATCH BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete Luiz Augusto von Dentz
                   ` (3 preceding siblings ...)
  2022-04-12  5:28 ` [PATCH BlueZ] " Paul Menzel
@ 2022-04-15  0:10 ` patchwork-bot+bluetooth
  4 siblings, 0 replies; 7+ messages in thread
From: patchwork-bot+bluetooth @ 2022-04-15  0:10 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Mon, 11 Apr 2022 16:58:02 -0700 you wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> 
> This fixes not clenup ssp_status and ssp_auto_complete flags on
> auth_complete.
> ---
>  emulator/btdev.c | 5 +++++
>  1 file changed, 5 insertions(+)

Here is the summary with links:
  - [BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=1967b5311525
  - [BlueZ,2/2] client/player: Add transport menu
    (no matching commit)

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2022-04-15  0:10 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-11 23:58 [PATCH BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete Luiz Augusto von Dentz
2022-04-11 23:58 ` [PATCH BlueZ 1/2] client/player: Add endpoint menu Luiz Augusto von Dentz
2022-04-12  2:52   ` [BlueZ,1/2] " bluez.test.bot
2022-04-11 23:58 ` [PATCH BlueZ 2/2] client/player: Add transport menu Luiz Augusto von Dentz
2022-04-12  1:56 ` [BlueZ] btdev: Fix not cleanup ssp_status and ssp_auto_complete bluez.test.bot
2022-04-12  5:28 ` [PATCH BlueZ] " Paul Menzel
2022-04-15  0:10 ` patchwork-bot+bluetooth

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).