Message ID | 20240423232622.1142256-1-ajay.k.v@intel.com |
---|---|
State | New |
Headers | show |
Series | [v1] bluetoothctl: CCP client interface for bluetoothctl | expand |
Dear Ajay, Thank you for your patch. I believe you should add BlueZ to the tag. It’d be also great if you made the commit message summary a statement by adding a verb in imperative mood. (The second bluetoothctl is also redundant.) Maybe: bluetoothctl: Add CCP clien interface Am 24.04.24 um 01:26 schrieb Ajay KV: > Allows bluetoothctl to send CCP client call control > commands like answer/reject call For such a big diff, it’d be great if you elaborated. Where can the specification be found? What is CCP, and give some example commands, and state how you tested it. > --- > 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 > 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" Shoudl the blank line stay? > /* 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); > 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" }, Should the elements be indented? > + {} }, > +}; > + > +static void call_added(GDBusProxy *proxy) Why not `ccp_add_call()` or `add_call()`? > +{ > + bt_shell_printf("[CHG] Telephony caller Added\n"); Lowercase: added. (Also below.) > + 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); > 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) What does ct mean? > +{ > + 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" > + > +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 > +}; > 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; Would be nice to make this a separate commit. Kind regards, Paul > + 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);
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 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); 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); 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" + +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 +}; 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);