Hi Ajay, On Tue, Apr 23, 2024 at 2:04 PM Ajay KV <ajay.k.v@xxxxxxxxx> wrote: > > Allows bluetoothctl to send CCP client call control > commands like answer/reject call > --- > Makefile.plugins | 3 +- > Makefile.tools | 4 +- > client/main.c | 4 +- > client/telephony_client.c | 213 +++++++++++++++++++++++++++++++ > client/telephony_client.h | 12 ++ > profiles/audio/ccp.c | 150 ++++++++++++++++++---- > profiles/audio/telephony.c | 173 ++++++++++++++++++++++++++ > profiles/audio/telephony.h | 65 ++++++++++ > src/shared/ccp.c | 249 +++++++++++++++++++++++-------------- > src/shared/ccp.h | 19 ++- > 10 files changed, 765 insertions(+), 127 deletions(-) > create mode 100644 client/telephony_client.c > create mode 100644 client/telephony_client.h > create mode 100644 profiles/audio/telephony.c > create mode 100644 profiles/audio/telephony.h > > diff --git a/Makefile.plugins b/Makefile.plugins > index 4aa2c9c92854..7c0e0bb23560 100644 > --- a/Makefile.plugins > +++ b/Makefile.plugins > @@ -46,7 +46,8 @@ builtin_modules += avrcp > builtin_sources += profiles/audio/control.h profiles/audio/control.c \ > profiles/audio/avctp.h profiles/audio/avctp.c \ > profiles/audio/avrcp.h profiles/audio/avrcp.c \ > - profiles/audio/player.h profiles/audio/player.c > + profiles/audio/player.h profiles/audio/player.c\ > + profiles/audio/telephony.c profiles/audio/telephony.h It should be named ccp.c so we don't give the impression we are going to write our own telefony stack, etc. > endif > > if NETWORK > diff --git a/Makefile.tools b/Makefile.tools > index 27a753762d1c..e21f6e8e478e 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/telephony_client.c \ > + client/telephony_client.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/main.c b/client/main.c > index 51d08a67aa1a..438c45bb9c24 100644 > --- a/client/main.c > +++ b/client/main.c > @@ -34,7 +34,7 @@ > #include "admin.h" > #include "player.h" > #include "mgmt.h" > - > +#include "telephony_client.h" > /* String display constants */ > #define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF > #define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF > @@ -3199,6 +3199,7 @@ int main(int argc, char *argv[]) > > admin_add_submenu(); > player_add_submenu(); > + telephony_add_submenu(); > mgmt_add_submenu(); > > client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez"); > @@ -3216,6 +3217,7 @@ int main(int argc, char *argv[]) > > admin_remove_submenu(); > player_remove_submenu(); > + telephony_remove_submenu(); > mgmt_remove_submenu(); > > g_dbus_client_unref(client); Replace telephony term with ccp. > diff --git a/client/telephony_client.c b/client/telephony_client.c > new file mode 100644 > index 000000000000..121255920dd0 > --- /dev/null > +++ b/client/telephony_client.c > @@ -0,0 +1,213 @@ > +// 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 "telephony_client.h" > + > +/* String display constants */ > +#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF > +#define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF > + > +#define BLUEZ_TELEPHONY_INTERFACE "org.bluez.telephonyCtrl" > + > +static DBusConnection *dbus_conn; > +static GDBusProxy *default_call = NULL; > +static GList *callList = NULL; > +static GDBusClient *client = NULL; > + > +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, "Telephony", 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 = "telephony", > + .desc = "telephony Settings Submenu", > + .entries = { > + { "answer", NULL, cmd_answer, "answer the active call" }, > + { "reject", NULL, cmd_reject, "reject the active call" }, > + {} }, > +}; > + > +static void call_added(GDBusProxy *proxy) > +{ > + bt_shell_printf("[CHG] Telephony caller Added\n"); > + callList = g_list_append(callList, proxy); > + > + if (!default_call) > + default_call = proxy; > + > + print_info(proxy, COLORED_NEW); > +} > + > +static void call_removed(GDBusProxy *proxy) > +{ > + bt_shell_printf("[CHG] Telephony 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_TELEPHONY_INTERFACE)) > + call_added(proxy); > +} > + > +static void proxy_removed(GDBusProxy *proxy, void *user_data) > +{ > + const char *interface; > + > + interface = g_dbus_proxy_get_interface(proxy); > + > + if (!strcmp(interface, BLUEZ_TELEPHONY_INTERFACE)) > + call_removed(proxy); > +} > + > +static void telephony_property_changed(GDBusProxy *proxy, const char *name, > + DBusMessageIter *iter) > +{ > + char *str; > + > + str = proxy_description(proxy, "Telephony", COLORED_CHG); > + print_iter(str, name, iter); > + g_free(str); > + > + bt_shell_printf("[CHG] Telephony 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_TELEPHONY_INTERFACE)) > + telephony_property_changed(proxy, name, iter); > +} > + > +void telephony_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 telephony_remove_submenu(void) > +{ > + g_dbus_client_unref(client); > +} > diff --git a/client/telephony_client.h b/client/telephony_client.h > new file mode 100644 > index 000000000000..15c73f0051e0 > --- /dev/null > +++ b/client/telephony_client.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 telephony_add_submenu(void); > +void telephony_remove_submenu(void); client/ccp.c and client/ccp.h shall be made into its own set of patches. > diff --git a/profiles/audio/ccp.c b/profiles/audio/ccp.c > index fe678de9fede..21f98cfbcfb7 100644 > --- a/profiles/audio/ccp.c > +++ b/profiles/audio/ccp.c > @@ -40,7 +40,6 @@ > #include "src/shared/gatt-db.h" > #include "src/shared/gatt-client.h" > #include "src/shared/gatt-server.h" > -#include "src/shared/ccp.h" > > #include "btio/btio.h" > #include "src/plugin.h" > @@ -51,6 +50,8 @@ > #include "src/service.h" > #include "src/log.h" > #include "src/error.h" > +#include "src/shared/ccp.h" > +#include "telephony.h" > > #define GTBS_UUID_STR "0000184C-0000-1000-8000-00805f9b34fb" > > @@ -58,9 +59,132 @@ struct ccp_data { > struct btd_device *device; > struct btd_service *service; > struct bt_ccp *ccp; > - unsigned int state_id; > + unsigned int call_state_id; > + struct telephony_ctrl *tc; > }; > > +static int ct_call_answer(struct telephony_ctrl *tc, void *user_data) > +{ > + struct bt_ccp *ccp = user_data; > + > + DBG(" status %d index %d", tc->call_status, tc->call_index); > + > + if (tc->call_status == CALL_DISCONNECTED) > + return -1; > + > + return bt_ccp_call_answer(ccp, tc->call_index); > +} > + > +static int ct_call_reject(struct telephony_ctrl *tc, void *user_data) > +{ > + struct bt_ccp *ccp = user_data; > + > + DBG(" status %d index %d", tc->call_status, tc->call_index); > + > + if (tc->call_status == CALL_DISCONNECTED) > + return -1; > + > + return bt_ccp_call_reject(ccp, tc->call_index); > +} > + > +static const struct telephony_control_callback ct_cbs = { > + .call_answer = &ct_call_answer, > + .call_reject = &ct_call_reject, > +}; > + > +static void cb_call_list_update(struct bt_ccp *ccp, const uint8_t *buf, > + uint16_t length) > +{ > + struct telephony_ctrl *tc = bt_ccp_get_user_data(ccp); > + struct ccp_call_list_evt *evt; > + > + DBG(""); > + > + if (length < sizeof(struct ccp_call_list_evt)) > + return; > + > + evt = (struct ccp_call_list_evt *)buf; > + tc->call_index = evt->index; > + tc->call_status = evt->state; > + > + DBG(" status %d index %d", tc->call_status, tc->call_index); > + > + telephony_update_call_Info(tc); > +} > + > +static void cb_incoming_call(struct bt_ccp *ccp, const uint8_t *buf, > + uint16_t length) > +{ > + struct ccp_incoming_call_evt *evt; > + struct telephony_ctrl *tc = bt_ccp_get_user_data(ccp); > + > + DBG(""); > + > + if (length < sizeof(struct ccp_incoming_call_evt)) > + return; > + > + evt = (struct ccp_incoming_call_evt *)buf; > + tc->call_index = evt->index; > + tc->call_status = INCOMING_CALL; > + > + DBG(" status %d index %d", tc->call_status, tc->call_index); > + > + telephony_update_call_Info(tc); > +} > + > +static void cb_terminate_call(struct bt_ccp *ccp, const uint8_t *buf, > + uint16_t length) > +{ > + struct ccp_call_terminate_evt *evt; > + struct telephony_ctrl *tc = bt_ccp_get_user_data(ccp); > + > + DBG(""); > + > + if (length < sizeof(struct ccp_call_terminate_evt)) > + return; > + > + evt = (struct ccp_call_terminate_evt *)buf; > + tc->call_index = evt->index; > + tc->call_status = CALL_DISCONNECTED; > + > + DBG(" status %d index %d", tc->call_status, tc->call_index); > + > + telephony_update_call_Info(tc); > +} > + > +static const struct bt_ccp_event_callback cbs = { > + .incoming_call = cb_incoming_call, > + .terminate_call = cb_terminate_call, > + .call_list_update = cb_call_list_update > +}; > + > +static int ccp_accept(struct btd_service *service) > +{ > + struct btd_device *device = btd_service_get_device(service); > + struct bt_gatt_client *client = btd_device_get_gatt_client(device); > + struct ccp_data *data = btd_service_get_user_data(service); > + char addr[18]; > + > + ba2str(device_get_address(device), addr); > + DBG("%s", addr); > + > + bt_ccp_attach(data->ccp, client); > + > + data->tc = telephony_create_device(device_get_path(device), 0); > + if (!data->tc) { > + DBG("Unable to create telephony device object"); > + data->tc = NULL; > + return -EINVAL; > + } > + > + telephony_set_callbacks(data->tc, &ct_cbs, data->ccp); > + bt_ccp_set_user_data(data->ccp, data->tc); > + bt_ccp_set_event_callbacks(data->ccp, &cbs, data->tc); > + btd_service_connecting_complete(service, 0); > + > + return 0; > +} > + > static void ccp_debug(const char *str, void *user_data) > { > DBG_IDX(0xffff, "%s", str); > @@ -140,28 +264,6 @@ static void ccp_remove(struct btd_service *service) > ccp_data_remove(data); > } > > -static int ccp_accept(struct btd_service *service) > -{ > - struct btd_device *device = btd_service_get_device(service); > - struct bt_gatt_client *client = btd_device_get_gatt_client(device); > - struct ccp_data *data = btd_service_get_user_data(service); > - char addr[18]; > - > - ba2str(device_get_address(device), addr); > - DBG("%s", addr); > - > - if (!bt_ccp_attach(data->ccp, client)) { > - error("CCP unable to attach"); > - return -EINVAL; > - } > - > - /* TODO: register telephony operations here */ > - > - btd_service_connecting_complete(service, 0); > - > - return 0; > -} > - > static int ccp_connect(struct btd_service *service) > { > struct btd_device *device = btd_service_get_device(service); > diff --git a/profiles/audio/telephony.c b/profiles/audio/telephony.c > new file mode 100644 > index 000000000000..68f27b5471cc > --- /dev/null > +++ b/profiles/audio/telephony.c > @@ -0,0 +1,173 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2024 Intel Corporation > + * > + */ > + > +#ifdef HAVE_CONFIG_H > +#include <config.h> > +#endif > + > +#define _GNU_SOURCE > +#include <stdlib.h> > +#include <stdint.h> > +#include <inttypes.h> > +#include <stdbool.h> > +#include <errno.h> > +#include <unistd.h> > +#include <string.h> > + > +#include <glib.h> > +#include <dbus/dbus.h> > +#include "gdbus/gdbus.h" > + > +#include "src/log.h" > +#include "src/dbus-common.h" > +#include "src/error.h" > +#include "telephony.h" > + > +#define BLUEZ_TELEPHONY_INTERFACE "org.bluez.telephonyCtrl" Don't thing we discussed about doing this sort of split of plugin just to implement a D-Bus interface, don't see any reason for these changes not to be part of ccp plugin, also the only reason for ccp plugin to exist is probably to implement the D-Bus interface, which shall document first under doc, and Id go with org.bluez.CCPTest1 as interface and all properties and methods shall be marked as experimental, in fact we may need a new tag for it since this interface shall only be exposed when bluetoothd is set to something like a test mode which can be used for validation purposes, and if that is not enabled then the test interfaces shall not be enabled either. > +struct call_callback { > + const struct telephony_control_callback *cbs; > + void *user_data; > +}; > + > +void telephony_update_call_Info(struct telephony_ctrl *tc) > +{ > + DBG(""); > + g_dbus_emit_property_changed(btd_get_dbus_connection(), tc->path, > + BLUEZ_TELEPHONY_INTERFACE, "call_state"); > +} > + > +static DBusMessage *telephony_answer_call(DBusConnection *conn, > + DBusMessage *msg, void *data) > +{ > + struct telephony_ctrl *tc = data; > + struct call_callback *cb = tc->cb; > + int err; > + > + DBG(""); > + if (!cb->cbs->call_answer) > + return btd_error_not_supported(msg); > + > + err = cb->cbs->call_answer(tc, cb->user_data); > + if (err < 0) > + return btd_error_failed(msg, strerror(-err)); > + > + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); > +} > + > +static DBusMessage *telephony_reject_call(DBusConnection *conn, > + DBusMessage *msg, void *data) > +{ > + struct telephony_ctrl *tc = data; > + struct call_callback *cb = tc->cb; > + int err; > + > + DBG(""); > + > + if (!cb->cbs->call_reject) > + return btd_error_not_supported(msg); > + > + err = cb->cbs->call_reject(tc, cb->user_data); > + if (err < 0) > + return btd_error_failed(msg, strerror(-err)); > + > + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); > +} > + > +static gboolean ccp_get_index(const GDBusPropertyTable *property, > + DBusMessageIter *iter, void *data) > +{ > + struct telephony_ctrl *tc = data; > + uint32_t index = tc->call_index; > + > + DBG(""); > + > + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &index); > + > + return TRUE; > +} > + > +static const GDBusSignalTable telephony_signals[] = { > +}; > + > +/* methods exposed to client to perform call operations */ > +static const GDBusMethodTable telephony_methods[] = { > + { GDBUS_METHOD("answer", NULL, NULL, telephony_answer_call) }, > + { GDBUS_METHOD("reject", NULL, NULL, telephony_reject_call) }, > + { } > +}; > + > +/* > + * Inform registered clients on property changed events > + * use g_dbus_emit_property_changed() API > + */ > +static const GDBusPropertyTable telephony_properties[] = { > + { "call_state", "u", ccp_get_index, NULL, NULL }, > + { } > +}; > + > +void telephony_destroy_device(struct telephony_ctrl *tc) > +{ > + DBG("%s", tc->path); > + > + g_dbus_unregister_interface(btd_get_dbus_connection(), > + tc->path, BLUEZ_TELEPHONY_INTERFACE); > + > + if (tc->path) > + g_free(tc->cb); > + if (tc->path) > + g_free(tc->path); > + if (tc->device) > + g_free(tc->device); > + > + if (tc) > + g_free(tc); > +} > + > +struct telephony_ctrl *telephony_create_device(const char *path, uint16_t id) > +{ > + struct telephony_ctrl *tc; > + > + DBG(""); > + tc = g_new0(struct telephony_ctrl, 1); > + tc->device = g_strdup(path); > + tc->path = g_strdup_printf("%s/Caller%u", path, id); > + > + if (!g_dbus_register_interface(btd_get_dbus_connection(), > + tc->path, BLUEZ_TELEPHONY_INTERFACE, > + telephony_methods, > + telephony_signals, > + telephony_properties, tc, NULL)) { > + error("D-Bus failed to register %s path", tc->path); > + telephony_destroy_device(tc); > + return NULL; > + } > + > + DBG("%s", tc->path); > + > + return tc; > +} > + > +void telephony_set_callbacks(struct telephony_ctrl *tp, > + const struct telephony_control_callback *cbs, > + void *user_data) > +{ > + struct call_callback *cb; > + > + DBG(""); > + > + if (tp->cb) > + g_free(tp->cb); > + > + cb = g_new0(struct call_callback, 1); > + cb->cbs = cbs; > + cb->user_data = user_data; > + > + tp->cb = cb; > +} > diff --git a/profiles/audio/telephony.h b/profiles/audio/telephony.h > new file mode 100644 > index 000000000000..e321fb881beb > --- /dev/null > +++ b/profiles/audio/telephony.h > @@ -0,0 +1,65 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2024 Intel Corporation > + * > + */ > + > +struct telephony_ctrl { > + char *device; /* Device path */ > + char *path; /* Telephony object path */ > + char *status; > + uint8_t call_status; /* call status of active call*/ > + uint8_t call_index; /* call index of active call */ > + struct call_callback *cb; > +}; > + > +struct telephony_control_callback { > + int (*call_answer)(struct telephony_ctrl *tc, void *user_data); > + int (*call_reject)(struct telephony_ctrl *tc, void *user_data); > +}; > + > +struct telephony_ctrl *telephony_create_device(const char *path, uint16_t id); > + > +void telephony_set_callbacks(struct telephony_ctrl *tc, > + const struct telephony_control_callback *cbs, > + void *user_data); > + > +void telephony_destroy_device(struct telephony_ctrl *tc); > + > +void telephony_set_incom_call_settings(struct telephony_ctrl *tc, > + const char *key, void *data, size_t len); > +void telephony_set_call_termination(struct telephony_ctrl *tc, > + const char *key, void *data, size_t len); > + > +void telephony_update_call_Info(struct telephony_ctrl *tc); > + > +struct ccp_call_list_evt { > + uint8_t length; > + uint8_t index; > + uint8_t state; > + uint8_t flag; > +}; > + > +struct ccp_incoming_call_evt { > + uint8_t length; > + uint8_t index; > +}; > + > +struct ccp_call_terminate_evt { > + uint8_t length; > + uint8_t index; > + uint8_t reason; > +}; > + > +enum call_state { > + INCOMING_CALL = 0, > + DIALLING_CALL, > + ALERTING_CALL, > + ACTIVE_CALL, > + LOCAL_HOLD, > + REMOTE_HOLD, > + CALL_DISCONNECTED = 10 > +}; shared/ccp shall be on its own patch as well. > diff --git a/src/shared/ccp.c b/src/shared/ccp.c > index 8e1b0b58f93b..25bb39713a13 100644 > --- a/src/shared/ccp.c > +++ b/src/shared/ccp.c > @@ -3,7 +3,7 @@ > * > * BlueZ - Bluetooth protocol stack for Linux > * > - * Copyright (C) 2022 Intel Corporation. All rights reserved. > + * Copyright (C) 2023 Intel Corporation. All rights reserved. > * > */ > > @@ -112,6 +112,55 @@ struct bt_ccs { > }; > > static struct queue *ccp_db; > +static struct bt_ccs *ccp_get_ccs(struct bt_ccp *ccp) > +{ > + if (!ccp) > + return NULL; > + > + if (ccp->rdb->ccs) > + return ccp->rdb->ccs; > + > + ccp->rdb->ccs = new0(struct bt_ccs, 1); > + ccp->rdb->ccs->mdb = ccp->rdb; > + > + return ccp->rdb->ccs; > +} > + > +static unsigned int ccp_send(struct bt_ccp *ccp, uint8_t index, > + uint8_t operation) > +{ > + int ret; > + uint16_t handle; > + uint8_t cmd[2]; > + struct bt_ccs *ccs = ccp_get_ccs(ccp); > + > + cmd[0] = operation; > + cmd[1] = index; > + > + if (!ccp->client) > + return -1; > + > + if (!gatt_db_attribute_get_char_data(ccs->call_ctrl_point, NULL, > + &handle, NULL, NULL, NULL)) > + return -1; > + > + ret = bt_gatt_client_write_without_response(ccp->client, handle, false, > + cmd, 2); > + if (!ret) > + return -1; > + > + return 0; > +} > + > +unsigned int bt_ccp_call_answer(struct bt_ccp *ccp, uint8_t index) > +{ > + return ccp_send(ccp, index, 0); > +} > + > +unsigned int bt_ccp_call_reject(struct bt_ccp *ccp, uint8_t index) > +{ > + return ccp_send(ccp, index, 1); > +} > > static void ccp_debug(struct bt_ccp *ccp, const char *format, ...) > { > @@ -429,20 +478,6 @@ static struct bt_ccs *ccs_new(struct gatt_db *db) > return ccs; > } > > -static struct bt_ccs *ccp_get_ccs(struct bt_ccp *ccp) > -{ > - if (!ccp) > - return NULL; > - > - if (ccp->rdb->ccs) > - return ccp->rdb->ccs; > - > - ccp->rdb->ccs = new0(struct bt_ccs, 1); > - ccp->rdb->ccs->mdb = ccp->rdb; > - > - return ccp->rdb->ccs; > -} > - > static void ccp_pending_destroy(void *data) > { > struct bt_ccp_pending *pending = data; > @@ -503,6 +538,8 @@ static void ccp_cb_register(uint16_t att_ecode, void *user_data) > { > struct bt_ccp *ccp = user_data; > > + DBG(ccp, ""); > + > if (att_ecode) > DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); > > @@ -515,27 +552,41 @@ static void ccp_cb_notify(uint16_t value_handle, const uint8_t *value, > /* TODO: generic handler for non-mandatory CCP notifications */ > } > > -static void ccp_cb_status_flag_register(uint16_t att_ecode, void *user_data) > +static void ccp_tc_update_call_list(struct bt_ccp *ccp, > + const uint8_t *value, uint16_t length) > { > - struct bt_ccp *ccp = user_data; > + struct event_callback *cb = ccp->cb; > > - if (att_ecode) > - DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); > + DBG(ccp, " "); > + > + if (cb && cb->cbs && cb->cbs->call_list_update) > + cb->cbs->call_list_update(ccp, value, length); > } > > -static void ccp_cb_status_flag_notify(uint16_t value_handle, > - const uint8_t *value, > - uint16_t length, void *user_data) > +static void ccp_tc_handle_incoming_call(struct bt_ccp *ccp, > + const uint8_t *value, uint16_t length) > { > - struct bt_ccp *ccp = user_data; > + struct event_callback *cb = ccp->cb; > > - DBG(ccp, ""); > + DBG(ccp, " "); > > - if (!length) > - return; > + if (cb && cb->cbs && cb->cbs->incoming_call) > + cb->cbs->incoming_call(ccp, value, length); > } > > -static void ccp_cb_terminate_register(uint16_t att_ecode, void *user_data) > +static void ccp_tc_handle_terminate_call(struct bt_ccp *ccp, > + const uint8_t *value, uint16_t length) > +{ > + struct event_callback *cb = ccp->cb; > + > + DBG(ccp, " "); > + > + if (cb && cb->cbs && cb->cbs->terminate_call) > + cb->cbs->terminate_call(ccp, value, length); > +} > + > +/* callback registered function */ > +static void ccp_cb_status_flag_register(uint16_t att_ecode, void *user_data) > { > struct bt_ccp *ccp = user_data; > > @@ -543,20 +594,15 @@ static void ccp_cb_terminate_register(uint16_t att_ecode, void *user_data) > DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); > } > > -static void ccp_cb_terminate_notify(uint16_t value_handle, const uint8_t *value, > - uint16_t length, void *user_data) > +static void ccp_cb_terminate_register(uint16_t att_ecode, void *user_data) > { > struct bt_ccp *ccp = user_data; > > - DBG(ccp, ""); > - > - if (!length) > - return; > - > - /* TODO: update call state in Local context */ > + if (att_ecode) > + DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); > } > > -static void ccp_cb_bearer_name_register(uint16_t att_ecode, void *user_data) > +static void ccp_cb_call_list_register(uint16_t att_ecode, void *user_data) > { > struct bt_ccp *ccp = user_data; > > @@ -566,21 +612,17 @@ static void ccp_cb_bearer_name_register(uint16_t att_ecode, void *user_data) > DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); > } > > -static void ccp_cb_bearer_name_notify(uint16_t value_handle, > - const uint8_t *value, > - uint16_t length, void *user_data) > +static void ccp_cb_call_state_register(uint16_t att_ecode, void *user_data) > { > struct bt_ccp *ccp = user_data; > > DBG(ccp, ""); > > - if (!length) > - return; > - > - /* TODO: update call details in Local context */ > + if (att_ecode) > + DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); > } > > -static void ccp_cb_call_list_register(uint16_t att_ecode, void *user_data) > +static void ccp_cb_incom_call_register(uint16_t att_ecode, void *user_data) > { > struct bt_ccp *ccp = user_data; > > @@ -590,51 +632,52 @@ static void ccp_cb_call_list_register(uint16_t att_ecode, void *user_data) > DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); > } > > -static void ccp_cb_call_list_notify(uint16_t value_handle, const uint8_t *value, > - uint16_t length, void *user_data) > +static void ccp_cb_bearer_name_register(uint16_t att_ecode, void *user_data) > { > struct bt_ccp *ccp = user_data; > > DBG(ccp, ""); > > - if (!length) > - return; > - > - /* TODO: update call list in Local context */ > + if (att_ecode) > + DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); > } > > -static void ccp_cb_call_state_register(uint16_t att_ecode, void *user_data) > +static void ccp_cb_status_flag_notify(uint16_t value_handle, > + const uint8_t *value, > + uint16_t length, void *user_data) > { > struct bt_ccp *ccp = user_data; > + DBG(ccp, "length %d", length); > > - DBG(ccp, ""); > - > - if (att_ecode) > - DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); > + if (!length) > + return; > } > > -static void ccp_cb_call_state_notify(uint16_t value_handle, > - const uint8_t *value, > - uint16_t length, void *user_data) > +static void ccp_cb_terminate_notify(uint16_t value_handle, > + const uint8_t *value, > + uint16_t length, void *user_data) > { > struct bt_ccp *ccp = user_data; > > - DBG(ccp, ""); > + DBG(ccp, "length %d", length); > > if (!length) > return; > > - /* TODO: update call state in Local context */ > + ccp_tc_handle_terminate_call(ccp, value, length); > } > > -static void ccp_cb_incom_call_register(uint16_t att_ecode, void *user_data) > +static void ccp_cb_call_list_notify(uint16_t value_handle, const uint8_t *value, > + uint16_t length, void *user_data) > { > struct bt_ccp *ccp = user_data; > > - DBG(ccp, ""); > + DBG(ccp, "length %d", length); > > - if (att_ecode) > - DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); > + if (!length) > + return; > + > + ccp_tc_update_call_list(ccp, value, length); > } > > static void ccp_cb_incom_call_notify(uint16_t value_handle, > @@ -643,12 +686,12 @@ static void ccp_cb_incom_call_notify(uint16_t value_handle, > { > struct bt_ccp *ccp = user_data; > > - DBG(ccp, ""); > + DBG(ccp, "length %d", length); > > if (!length) > return; > > - /* TODO: Handle incoming call notofiation, Answer/reject etc */ > + ccp_tc_handle_incoming_call(ccp, value, length); > } > > static void bt_ccp_incom_call_attach(struct bt_ccp *ccp) > @@ -691,7 +734,7 @@ static void bt_ccp_call_state_attach(struct bt_ccp *ccp) > bt_gatt_client_register_notify(ccp->client, > value_handle, > ccp_cb_call_state_register, > - ccp_cb_call_state_notify, ccp, > + NULL, ccp, > NULL); > } > > @@ -735,7 +778,7 @@ static void bt_ccp_name_attach(struct bt_ccp *ccp) > bt_gatt_client_register_notify(ccp->client, > value_handle, > ccp_cb_bearer_name_register, > - ccp_cb_bearer_name_notify, ccp, > + NULL, ccp, > NULL); > } > > @@ -799,7 +842,7 @@ static void bt_ccp_uci_attach(struct bt_ccp *ccp) > ccp->bearer_uci_id = bt_gatt_client_register_notify(ccp->client, > value_handle, > ccp_cb_register, > - ccp_cb_notify, ccp, > + NULL, ccp, > NULL); > } > > @@ -820,7 +863,7 @@ static void bt_ccp_technology_attach(struct bt_ccp *ccp) > ccp->bearer_technology_id = > bt_gatt_client_register_notify(ccp->client, > value_handle, ccp_cb_register, > - ccp_cb_notify, ccp, NULL); > + NULL, ccp, NULL); > } > > static void bt_ccp_strength_attach(struct bt_ccp *ccp) > @@ -839,7 +882,7 @@ static void bt_ccp_strength_attach(struct bt_ccp *ccp) > > ccp->signal_strength_id = > bt_gatt_client_register_notify(ccp->client, value_handle, > - ccp_cb_register, ccp_cb_notify, > + ccp_cb_register, NULL, > ccp, NULL); > } > > @@ -859,7 +902,7 @@ static void bt_ccp_ccid_attach(struct bt_ccp *ccp) > ccp->ccid_id = bt_gatt_client_register_notify(ccp->client, > value_handle, > ccp_cb_register, > - ccp_cb_notify, ccp, NULL); > + NULL, ccp, NULL); > } > > static void bt_ccp_tar_uri_attach(struct bt_ccp *ccp) > @@ -879,7 +922,7 @@ static void bt_ccp_tar_uri_attach(struct bt_ccp *ccp) > ccp->target_bearer_uri_id = > bt_gatt_client_register_notify(ccp->client, > value_handle, ccp_cb_register, > - ccp_cb_notify, ccp, > + NULL, ccp, > NULL); > } > > @@ -900,7 +943,7 @@ static void bt_ccp_ctrl_point_attach(struct bt_ccp *ccp) > ccp->call_control_pt_id = > bt_gatt_client_register_notify(ccp->client, > value_handle, ccp_cb_register, > - ccp_cb_notify, ccp, NULL); > + NULL, ccp, NULL); > } > > static void bt_ccp_ctrl_opcode_attach(struct bt_ccp *ccp) > @@ -920,7 +963,7 @@ static void bt_ccp_ctrl_opcode_attach(struct bt_ccp *ccp) > ccp->call_control_opt_opcode_id = > bt_gatt_client_register_notify(ccp->client, > value_handle, ccp_cb_register, > - ccp_cb_notify, ccp, NULL); > + NULL, ccp, NULL); > } > > static void bt_ccp_friendly_name_attach(struct bt_ccp *ccp) > @@ -940,7 +983,7 @@ static void bt_ccp_friendly_name_attach(struct bt_ccp *ccp) > ccp->friendly_name_id = > bt_gatt_client_register_notify(ccp->client, > value_handle, ccp_cb_register, > - ccp_cb_notify, ccp, NULL); > + NULL, ccp, NULL); > } > > static void bt_ccp_signal_intrvl_attach(struct bt_ccp *ccp) > @@ -960,7 +1003,7 @@ static void bt_ccp_signal_intrvl_attach(struct bt_ccp *ccp) > ccp->signal_reporting_intrvl_id = > bt_gatt_client_register_notify(ccp->client, > value_handle, ccp_cb_register, > - ccp_cb_notify, ccp, NULL); > + NULL, ccp, NULL); > } > > static void bt_ccp_uri_list_attach(struct bt_ccp *ccp) > @@ -980,7 +1023,7 @@ static void bt_ccp_uri_list_attach(struct bt_ccp *ccp) > ccp->bearer_uri_schemes_list_id = > bt_gatt_client_register_notify(ccp->client, > value_handle, ccp_cb_register, > - ccp_cb_notify, ccp, NULL); > + NULL, ccp, NULL); > } > > static void foreach_ccs_char(struct gatt_db_attribute *attr, void *user_data) > @@ -988,7 +1031,8 @@ static void foreach_ccs_char(struct gatt_db_attribute *attr, void *user_data) > struct bt_ccp *ccp = user_data; > struct bt_ccs *ccs; > uint16_t value_handle; > - bt_uuid_t uuid; > + bt_uuid_t uuid, uuid16; > + uint16_t be16; > > if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, > NULL, NULL, &uuid)) > @@ -998,105 +1042,122 @@ static void foreach_ccs_char(struct gatt_db_attribute *attr, void *user_data) > if (!ccs || ccs->call_state) > return; > > - if (bt_uuid16_cmp(&uuid, BEARER_PROVIDER_NAME_CHRC_UUID)) { > + uuid16.type = uuid.type; > + > + if (uuid.type == BT_UUID16) { > + DBG(ccp, "uuid %x", uuid.value.u16); > + uuid16.value.u16 = uuid.value.u16; > + } else if (uuid.type == BT_UUID128) { > + DBG(ccp, "uuid is u128 "); > + uuid16.type = BT_UUID16; > + memcpy(&be16, &uuid.value.u128.data[2], 2); > + uuid16.value.u16 = htons(be16); > + } else { > + DBG(ccp, "unexpected uuid type %d", uuid16.type); > + return; > + } > + > + DBG(ccp, "uuid read from gatt database %x", uuid16.value.u16); > + > + if (bt_uuid16_cmp(&uuid16, BEARER_PROVIDER_NAME_CHRC_UUID)) { > DBG(ccp, "Found Bearer Name, handle 0x%04x", value_handle); > > ccs->bearer_name = attr; > bt_ccp_name_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, BEARER_UCI_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, BEARER_UCI_CHRC_UUID)) { > DBG(ccp, "Found Bearer Uci, handle 0x%04x", value_handle); > > ccs->bearer_uci = attr; > bt_ccp_uci_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, BEARER_TECH_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, BEARER_TECH_CHRC_UUID)) { > DBG(ccp, "Found Bearer Technology, handle %x", value_handle); > > ccs->bearer_technology = attr; > bt_ccp_technology_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, BEARER_SIGNAL_STR_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, BEARER_SIGNAL_STR_CHRC_UUID)) { > DBG(ccp, "Found Signal Strength, handle 0x%04x", value_handle); > > ccs->signal_strength = attr; > bt_ccp_strength_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, BEARER_SIGNAL_INTRVL_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, BEARER_SIGNAL_INTRVL_CHRC_UUID)) { > DBG(ccp, "Found Signal Interval, handle 0x%04x", value_handle); > > ccs->signal_reporting_intrvl = attr; > bt_ccp_signal_intrvl_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, CALL_STATUS_FLAG_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, CALL_STATUS_FLAG_CHRC_UUID)) { > DBG(ccp, "Found Status Flag, handle 0x%04x", value_handle); > > ccs->status_flag = attr; > bt_ccp_status_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, BEARER_URI_SCHEME_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, BEARER_URI_SCHEME_CHRC_UUID)) { > DBG(ccp, "Found URI Scheme, handle 0x%04x", value_handle); > > ccs->bearer_uri_schemes_list = attr; > bt_ccp_uri_list_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, CURR_CALL_LIST_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, CURR_CALL_LIST_CHRC_UUID)) { > DBG(ccp, "Found Call List, handle 0x%04x", value_handle); > > ccs->current_call_list = attr; > bt_ccp_call_list_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, BEARER_CCID_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, BEARER_CCID_CHRC_UUID)) { > DBG(ccp, "Found CCID, handle 0x%04x", value_handle); > > ccs->ccid = attr; > bt_ccp_ccid_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, INCOM_CALL_TARGET_URI_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, INCOM_CALL_TARGET_URI_CHRC_UUID)) { > DBG(ccp, "Found Bearer Uri, handle 0x%04x", value_handle); > > ccs->target_bearer_uri = attr; > bt_ccp_tar_uri_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, CALL_CTRL_POINT_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, CALL_CTRL_POINT_CHRC_UUID)) { > DBG(ccp, "Found Control Point, handle 0x%04x", value_handle); > > ccs->call_ctrl_point = attr; > bt_ccp_ctrl_point_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, CALL_CTRL_POINT_OPT_OPCODE_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, CALL_CTRL_POINT_OPT_OPCODE_CHRC_UUID)) { > DBG(ccp, "Found Control opcode, handle 0x%04x", value_handle); > > ccs->call_ctrl_opt_opcode = attr; > bt_ccp_ctrl_opcode_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, TERMINATION_REASON_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, TERMINATION_REASON_CHRC_UUID)) { > DBG(ccp, "Found Termination Reason, handle %x", value_handle); > > ccs->termination_reason = attr; > bt_ccp_term_reason_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, INCOMING_CALL_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, INCOMING_CALL_CHRC_UUID)) { > DBG(ccp, "Found Incoming call, handle 0x%04x", value_handle); > > ccs->incoming_call = attr; > bt_ccp_incom_call_attach(ccp); > } > > - if (bt_uuid16_cmp(&uuid, CALL_FRIENDLY_NAME_CHRC_UUID)) { > + if (bt_uuid16_cmp(&uuid16, CALL_FRIENDLY_NAME_CHRC_UUID)) { > DBG(ccp, "Found Friendly name, handle 0x%04x", value_handle); > > ccs->friendly_name = attr; > diff --git a/src/shared/ccp.h b/src/shared/ccp.h > index 28b8b034ece3..3298abe9014c 100644 > --- a/src/shared/ccp.h > +++ b/src/shared/ccp.h > @@ -3,7 +3,7 @@ > * > * BlueZ - Bluetooth protocol stack for Linux > * > - * Copyright (C) 2020 Intel Corporation. All rights reserved. > + * Copyright (C) 2023 Intel Corporation. All rights reserved. > * > */ > > @@ -18,14 +18,18 @@ struct bt_ccp; > struct bt_ccp_db; > struct bt_ccp_session_info; > > -typedef void (*bt_ccp_debug_func_t)(const char *str, void *user_data); > -typedef void (*bt_ccp_destroy_func_t)(void *user_data); > - > struct bt_ccp_event_callback { > - void (*call_state)(struct bt_ccp *ccp, const uint8_t *value, > - uint16_t length); > + void (*incoming_call)(struct bt_ccp *ccp, > + const uint8_t *value, uint16_t len); > + void (*terminate_call)(struct bt_ccp *ccp, > + const uint8_t *value, uint16_t len); > + void (*call_list_update)(struct bt_ccp *ccp, > + const uint8_t *value, uint16_t len); > }; > > +typedef void (*bt_ccp_debug_func_t)(const char *str, void *user_data); > +typedef void (*bt_ccp_destroy_func_t)(void *user_data); > + > void bt_ccp_set_event_callbacks(struct bt_ccp *ccp, > const struct bt_ccp_event_callback *cbs, > void *user_data); > @@ -43,3 +47,6 @@ void bt_ccp_unref(struct bt_ccp *ccp); > > bool bt_ccp_set_user_data(struct bt_ccp *ccp, void *user_data); > void *bt_ccp_get_user_data(struct bt_ccp *ccp); > + > +unsigned int bt_ccp_call_answer(struct bt_ccp *ccp, uint8_t index); > +unsigned int bt_ccp_call_reject(struct bt_ccp *ccp, uint8_t index); > -- > 2.34.1 > > -- Luiz Augusto von Dentz