Hi Ajay, On Mon, May 6, 2024 at 2:30 PM Ajay KV <ajay.k.v@xxxxxxxxx> wrote: > > This adds submeu in bluetoothctl for CCP Testing with > options like answer and reject the active call . This feature > is tested with windows machnine as CCP server which uses Teams > application to make calls > > Signed-off-by: Ajay KV <ajay.k.v@xxxxxxxxx> > --- > Makefile.tools | 4 +- > client/ccp_test.c | 212 ++++++++++++++++++++++++++++++++++++++++++++++ > client/ccp_test.h | 12 +++ > client/main.c | 5 +- > 4 files changed, 231 insertions(+), 2 deletions(-) > create mode 100644 client/ccp_test.c > create mode 100644 client/ccp_test.h > > diff --git a/Makefile.tools b/Makefile.tools > index 679c914bf8cd..a5587427f549 100644 > --- a/Makefile.tools > +++ b/Makefile.tools > @@ -13,7 +13,9 @@ client_bluetoothctl_SOURCES = client/main.c \ > client/gatt.h client/gatt.c \ > client/admin.h client/admin.c \ > client/player.h client/player.c \ > - client/mgmt.h client/mgmt.c > + client/mgmt.h client/mgmt.c \ > + client/ccp_test.c \ > + client/ccp_test.h > client_bluetoothctl_LDADD = lib/libbluetooth-internal.la \ > gdbus/libgdbus-internal.la src/libshared-glib.la \ > $(GLIB_LIBS) $(DBUS_LIBS) -lreadline > diff --git a/client/ccp_test.c b/client/ccp_test.c > new file mode 100644 > index 000000000000..d53fc2393c13 > --- /dev/null > +++ b/client/ccp_test.c > @@ -0,0 +1,212 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2024 Intel Corporation. All rights reserved. > + * > + */ > + > +#ifdef HAVE_CONFIG_H > +#include <config.h> > +#endif > + > +#define _GNU_SOURCE > +#include <stdio.h> > +#include <stdlib.h> > +#include "gdbus/gdbus.h" > +#include "lib/bluetooth.h" > +#include "src/shared/shell.h" > +#include "print.h" > +#include "ccp_test.h" > + > +/* String display constants */ > +#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF > +#define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF > + > +#define BLUEZ_CCP_TEST_INTERFACE "org.bluez.CCPTest1" We probably need to start with the documentation of org.bluez.CPPTest1, there is quite a few documentation under doc/org.bluez*.rst. > + > +static DBusConnection *dbus_conn; > +static GDBusProxy *default_call; > +static GList *callList; > +static GDBusClient *client; > + > +static char *proxy_description(GDBusProxy *proxy, const char *title, > + const char *description) > +{ > + const char *path; > + > + path = g_dbus_proxy_get_path(proxy); > + return g_strdup_printf("%s%s%s%s %s ", > + description ? "[" : "", > + description ? : "", > + description ? "] " : "", > + title, path); > +} > + > +static void print_info(void *data, void *user_data) > +{ > + GDBusProxy *proxy = data; > + const char *description = user_data; > + char *str; > + > + str = proxy_description(proxy, "CCPTest", description); > + > + bt_shell_printf("%s%s\n", str, > + default_call == proxy ? "[default]" : ""); > + > + g_free(str); > +} > + > +static void call_reject_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 reject call: %s\n", error.name); > + dbus_error_free(&error); > + return bt_shell_noninteractive_quit(EXIT_FAILURE); > + } > + > + bt_shell_printf("operation completed\n"); > + > + return bt_shell_noninteractive_quit(EXIT_SUCCESS); > +} > + > +static void cmd_reject(int argc, char *argv[]) > +{ > + if (!default_call) { > + bt_shell_printf("No active calls present\n"); > + return bt_shell_noninteractive_quit(EXIT_FAILURE); > + } > + > + if (g_dbus_proxy_method_call(default_call, "reject", NULL, > + call_reject_reply, NULL, NULL) == FALSE) { > + bt_shell_printf("Failed to reject call\n"); > + return bt_shell_noninteractive_quit(EXIT_FAILURE); > + } > +} > + > +static void call_answer_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 answer call: %s\n", error.name); > + dbus_error_free(&error); > + return bt_shell_noninteractive_quit(EXIT_FAILURE); > + } > + > + bt_shell_printf("operation completed\n"); > + > + return bt_shell_noninteractive_quit(EXIT_SUCCESS); > +} > + > +static void cmd_answer(int argc, char *argv[]) > +{ > + if (!default_call) > + return bt_shell_noninteractive_quit(EXIT_FAILURE); > + > + if (g_dbus_proxy_method_call(default_call, "answer", NULL, > + call_answer_reply, NULL, NULL) == FALSE) { > + bt_shell_printf("Failed to answer the call\n"); > + return bt_shell_noninteractive_quit(EXIT_FAILURE); > + } > +} > + > +static const struct bt_shell_menu call_menu = { > + .name = "ccp test", I'd call it just ccp otherwise you may break autocomplete support by adding spaces in between, in case you don't know bluetoothctl does accept entering commands in a submenu.command format (e.g. cpp.answer). > + .desc = "ccp test settings submenu", > + .entries = { > + { "answer", NULL, cmd_answer, "answer the active call" }, > + { "reject", NULL, cmd_reject, "reject the active call" }, > + }, I guess we will need to add something like as "[call]" as parameter if the intention is handle multiple calls simultaneously which if I recall correctly is possible with CCP, btw I add as well command such as list, to list current call objects and perhap a show command as well to inspect the objects, but that can be done at a later stage if you prefer that way. > +}; > + > +static void ccp_add_call(GDBusProxy *proxy) > +{ > + bt_shell_printf("[CHG] CCP Test caller added\n"); > + callList = g_list_append(callList, proxy); > + > + if (!default_call) > + default_call = proxy; > + > + print_info(proxy, COLORED_NEW); > +} > + > +static void ccp_remove_call(GDBusProxy *proxy) > +{ > + bt_shell_printf("[CHG] CCP Test caller removed\n"); > + > + if (default_call == proxy) > + default_call = NULL; > + > + callList = g_list_remove(callList, proxy); > +} > + > +static void proxy_added(GDBusProxy *proxy, void *user_data) > +{ > + const char *interface; > + > + interface = g_dbus_proxy_get_interface(proxy); > + > + if (!strcmp(interface, BLUEZ_CCP_TEST_INTERFACE)) > + ccp_add_call(proxy); > +} > + > +static void proxy_removed(GDBusProxy *proxy, void *user_data) > +{ > + const char *interface; > + > + interface = g_dbus_proxy_get_interface(proxy); > + > + if (!strcmp(interface, BLUEZ_CCP_TEST_INTERFACE)) > + ccp_remove_call(proxy); > +} > + > +static void ccptest_property_changed(GDBusProxy *proxy, const char *name, > + DBusMessageIter *iter) > +{ > + char *str; > + > + str = proxy_description(proxy, "CCP Test", COLORED_CHG); > + print_iter(str, name, iter); > + g_free(str); > + > + bt_shell_printf("[CHG] CCP Test property : %s\n", name); > +} > + > +static void property_changed(GDBusProxy *proxy, const char *name, > + DBusMessageIter *iter, void *user_data) > +{ > + const char *interface; > + > + interface = g_dbus_proxy_get_interface(proxy); > + > + if (!strcmp(interface, BLUEZ_CCP_TEST_INTERFACE)) > + ccptest_property_changed(proxy, name, iter); > +} > + > +void ccptest_add_submenu(void) > +{ > + bt_shell_add_submenu(&call_menu); > + > + dbus_conn = bt_shell_get_env("DBUS_CONNECTION"); > + if (!dbus_conn || client) > + return; > + > + client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez"); > + > + g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed, > + property_changed, NULL); > + g_dbus_client_set_disconnect_watch(client, NULL, NULL); > +} > + > +void ccptest_remove_submenu(void) > +{ > + g_dbus_client_unref(client); > +} > diff --git a/client/ccp_test.h b/client/ccp_test.h > new file mode 100644 > index 000000000000..fc2ab2042bb8 > --- /dev/null > +++ b/client/ccp_test.h > @@ -0,0 +1,12 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2024 Intel Corporation. All rights reserved. > + * > + * > + */ > + > +void ccptest_add_submenu(void); > +void ccptest_remove_submenu(void); > diff --git a/client/main.c b/client/main.c > index c8b0f7f1c2d8..dba6dea639d9 100644 > --- a/client/main.c > +++ b/client/main.c > @@ -34,6 +34,7 @@ > #include "admin.h" > #include "player.h" > #include "mgmt.h" > +#include "ccp_test.h" > > /* String display constants */ > #define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF > @@ -3060,7 +3061,7 @@ static const struct bt_shell_menu gatt_menu = { > "Unregister application service" }, > { "register-includes", "<UUID> [handle]", cmd_register_includes, > "Register as Included service in." }, > - { "unregister-includes", "<Service-UUID> <Inc-UUID>", > + { "unregister-includes", "<Service-UUID><Inc-UUID>", > cmd_unregister_includes, > "Unregister Included service." }, > { "register-characteristic", > @@ -3199,6 +3200,7 @@ int main(int argc, char *argv[]) > > admin_add_submenu(); > player_add_submenu(); > + ccptest_add_submenu(); > mgmt_add_submenu(); > > client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez"); > @@ -3216,6 +3218,7 @@ int main(int argc, char *argv[]) > > admin_remove_submenu(); > player_remove_submenu(); > + ccptest_remove_submenu(); > mgmt_remove_submenu(); > > g_dbus_client_unref(client); > -- > 2.34.1 > > -- Luiz Augusto von Dentz