- Includes implementations required for PTS testing for MICS and MICP. - Interface given in bluetoothctl/player menu for sending MICS and MICP commands while excecuting PTS test cases. Tested all MICP and MICS PTS test cases[LE] and all are passed. - Added flag - MICP_MICS_PTS_FLAG in configure.ac which enable/disable PTS testing related code during compilation. uncomment this flag in configure.ac inorder to enable PTS testing related code and comment it for disable. By default this flag is disabled. - Spec implementation/PTS testing: MICS - MICS_v1.0.pdf MICP - MICP_v1.0.pdf PTS Testing MICS: MICS.TS.p0ed2.pdf PTS Testing MICP: MICP.TS.p3.pdf --- client/main.c | 12 ++++ client/player.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++ client/player.h | 4 ++ configure.ac | 3 + src/adapter.c | 87 ++++++++++++++++++++++++ src/shared/micp.c | 122 ++++++++++++++++++++++++++++++++++ src/shared/micp.h | 8 +++ 7 files changed, 400 insertions(+) diff --git a/client/main.c b/client/main.c index 0eac5bdf5..d7c735d19 100644 --- a/client/main.c +++ b/client/main.c @@ -413,6 +413,10 @@ static struct adapter *adapter_new(GDBusProxy *proxy) if (!default_ctrl) default_ctrl = adapter; +#ifdef MICP_MICS_PTS_FLAG + mics_set_proxy((void *)adapter); +#endif /* MICP_MICS_PTS_FLAG */ + return adapter; } @@ -892,6 +896,10 @@ static void cmd_show(int argc, char *argv[]) } } +#ifdef MICP_MICS_PTS_FLAG + mics_set_proxy((void *)adapter); +#endif /*MICP_MICS_PTS_FLAG*/ + if (!g_dbus_proxy_get_property(adapter->proxy, "Address", &iter)) return bt_shell_noninteractive_quit(EXIT_FAILURE); @@ -951,6 +959,10 @@ static void cmd_select(int argc, char *argv[]) return bt_shell_noninteractive_quit(EXIT_FAILURE); } +#ifdef MICP_MICS_PTS_FLAG + mics_set_proxy((void *)adapter); +#endif /*MICP_MICS_PTS_FLAG*/ + if (default_ctrl && default_ctrl->proxy == adapter->proxy) return bt_shell_noninteractive_quit(EXIT_SUCCESS); diff --git a/client/player.c b/client/player.c index e5084967a..2e48025e8 100644 --- a/client/player.c +++ b/client/player.c @@ -596,6 +596,153 @@ static void cmd_show_item(int argc, char *argv[]) return bt_shell_noninteractive_quit(EXIT_SUCCESS); } +#ifdef MICP_MICS_PTS_FLAG +struct mics_adapter { + GDBusProxy *proxy; +}; +static struct mics_adapter *mics_default_ctrl; +void mics_set_proxy(void *proxy) +{ + mics_default_ctrl = (struct mics_adapter *)proxy; + if (mics_default_ctrl == NULL) { + bt_shell_printf("mics_default_ctrl is NULL\n"); + return; + } +} +static gboolean parse_argument(int argc, char *argv[], const char **arg_table, + const char *msg, dbus_bool_t *value, + const char **option) +{ + const char **opt; + + if (!strcmp(argv[1], "help")) { + for (opt = arg_table; opt && *opt; opt++) + bt_shell_printf("%s\n", *opt); + bt_shell_noninteractive_quit(EXIT_SUCCESS); + return FALSE; + } + + if (!strcmp(argv[1], "on") || !strcmp(argv[1], "yes")) { + *value = TRUE; + if (option) + *option = ""; + return TRUE; + } + + if (!strcmp(argv[1], "off") || !strcmp(argv[1], "no")) { + *value = FALSE; + return TRUE; + } + + for (opt = arg_table; opt && *opt; opt++) { + if (strcmp(argv[1], *opt) == 0) { + *value = TRUE; + *option = *opt; + return TRUE; + } + } + + bt_shell_printf("Invalid argument %s\n", argv[1]); + return FALSE; +} + +static void cmd_set_mute_state(int argc, char *argv[]) +{ + dbus_bool_t mute_state; + char *str; + + if (!parse_argument(argc, argv, NULL, NULL, &mute_state, NULL)) + return bt_shell_noninteractive_quit(EXIT_FAILURE); + + str = g_strdup_printf("mics %s", mute_state == TRUE ? "on" : "off"); + + if (g_dbus_proxy_set_property_basic(mics_default_ctrl->proxy, "mics", + DBUS_TYPE_BOOLEAN, &mute_state, + generic_callback, str, g_free) == TRUE) + return; + g_free(str); + + return bt_shell_noninteractive_quit(EXIT_FAILURE); +} + +static void cmd_enable_disable_mute_state(int argc, char *argv[]) +{ + dbus_bool_t mute_state; + char *str; + + if (!parse_argument(argc, argv, NULL, NULL, &mute_state, NULL)) + return bt_shell_noninteractive_quit(EXIT_FAILURE); + + str = g_strdup_printf("mics %s", mute_state == TRUE ? "on" : "off"); + + if (g_dbus_proxy_set_property_basic(mics_default_ctrl->proxy, + "mics_state", DBUS_TYPE_BOOLEAN, &mute_state, + generic_callback, str, g_free) == TRUE) + return; + g_free(str); + + return bt_shell_noninteractive_quit(EXIT_FAILURE); +} + +static void cmd_micp_discover_mute(int argc, char *argv[]) +{ + dbus_bool_t mute_state = 0; + char *str; + + + if (!parse_argument(argc, argv, NULL, NULL, &mute_state, NULL)) + return bt_shell_noninteractive_quit(EXIT_FAILURE); + + str = g_strdup_printf("mics %s", mute_state == TRUE ? "on" : "off"); + + if (g_dbus_proxy_set_property_basic(mics_default_ctrl->proxy, + "micp_disc", DBUS_TYPE_BOOLEAN, &mute_state, + generic_callback, str, g_free) == TRUE) + return; + g_free(str); + + return bt_shell_noninteractive_quit(EXIT_FAILURE); +} + +static void cmd_enable_read_mute_state(int argc, char *argv[]) +{ + char *endptr = NULL; + int handle; + + handle = strtol(argv[1], &endptr, 0); + if (!endptr || *endptr != '\0' || handle > UINT16_MAX) { + bt_shell_printf("Invalid argument: %s\n", argv[1]); + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } + bt_shell_printf("%s: %x\n", __func__, handle); + if (g_dbus_proxy_set_property_basic(mics_default_ctrl->proxy, + "micp_read_char", DBUS_TYPE_UINT16, &handle, + generic_callback, NULL, NULL) == TRUE) + return; + + return bt_shell_noninteractive_quit(EXIT_FAILURE); +} + +static void cmd_enable_write_mute_state(int argc, char *argv[]) +{ + char *endptr = NULL; + int handle; + + handle = strtol(argv[1], &endptr, 0); + if (!endptr || *endptr != '\0' || handle > UINT16_MAX) { + bt_shell_printf("Invalid argument: %s\n", argv[1]); + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } + bt_shell_printf("%s : %x\n", __func__, handle); + if (g_dbus_proxy_set_property_basic(mics_default_ctrl->proxy, + "micp_write_char", DBUS_TYPE_UINT16, &handle, + generic_callback, NULL, NULL) == TRUE) + return; + + return bt_shell_noninteractive_quit(EXIT_FAILURE); +} +#endif /* MICP_MICS_PTS_FLAG */ + static void cmd_show(int argc, char *argv[]) { GDBusProxy *proxy; @@ -969,6 +1116,23 @@ static const struct bt_shell_menu player_menu = { item_generator}, { "show-item", "<item>", cmd_show_item, "Show item information", item_generator}, +#ifdef MICP_MICS_PTS_FLAG + { "mics_mute", "<on/off>", cmd_set_mute_state, + "Set Mics Mute state to on / off", + NULL }, + { "mics_state", "<on/off>", cmd_enable_disable_mute_state, + "Set Mics Mute state to on[enable] / off[disable]", + NULL }, + { "micp_discover", "<on/off>", cmd_micp_discover_mute, + "discover Mute Characteristic", + NULL }, + { "micp_read", "<handle>", cmd_enable_read_mute_state, + "Read Mute Characteristic", + NULL }, + { "micp_write", "<handle>", cmd_enable_write_mute_state, + "Write Mute Characteristic", + NULL }, +#endif /* MICP_MICS_PTS_FLAG */ {} }, }; diff --git a/client/player.h b/client/player.h index e7778cb1e..316090721 100644 --- a/client/player.h +++ b/client/player.h @@ -10,3 +10,7 @@ void player_add_submenu(void); void player_remove_submenu(void); + +#ifdef MICP_MICS_PTS_FLAG +void mics_set_proxy(void *proxy); +#endif /*MICP_MICS_PTS_FLAG*/ diff --git a/configure.ac b/configure.ac index 9a8856380..a190d9168 100644 --- a/configure.ac +++ b/configure.ac @@ -215,6 +215,9 @@ AC_ARG_ENABLE(micp, AS_HELP_STRING([--disable-micp], [disable MICP profile]), [enable_micp=${enableval}]) AM_CONDITIONAL(MICP, test "${enable_micp}" != "no") +#AC_DEFINE(MICP_MICS_PTS_FLAG, 1, +# [Enable/Disable PTS related code changes in MICP and MICS]) + AC_ARG_ENABLE(csip, AS_HELP_STRING([--disable-csip], [disable CSIP profile]), [enable_csip=${enableval}]) AM_CONDITIONAL(CSIP, test "${enable_csip}" != "no") diff --git a/src/adapter.c b/src/adapter.c index 2679d4302..89f6d76f4 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -68,6 +68,10 @@ #include "eir.h" #include "battery.h" +#ifdef MICP_MICS_PTS_FLAG +#include "src/shared/micp.h" +#endif /*MICP_MICS_PTS_FLAG*/ + #define MODE_OFF 0x00 #define MODE_CONNECTABLE 0x01 #define MODE_DISCOVERABLE 0x02 @@ -3333,6 +3337,82 @@ static void property_set_pairable(const GDBusPropertyTable *property, property_set_mode(adapter, MGMT_SETTING_BONDABLE, iter, id); } +#ifdef MICP_MICS_PTS_FLAG +static void property_set_mute_state(const GDBusPropertyTable *property, + DBusMessageIter *iter, + GDBusPendingPropertySet id, void *user_data) +{ + dbus_bool_t enable; + + dbus_message_iter_get_basic(iter, &enable); + DBG("SET %s: %d\n", __func__, enable); + mics_change_mute_state(enable); + g_dbus_pending_property_success(id); +} + +static void property_mute_enable_disable(const GDBusPropertyTable *propert, + DBusMessageIter *iter, + GDBusPendingPropertySet id, void *user_data) +{ + dbus_bool_t enable; + + dbus_message_iter_get_basic(iter, &enable); + DBG("%s: %d\n", __func__, enable); + mics_enable_disable_mute(enable); + g_dbus_pending_property_success(id); +} + +static void property_micp_discover_mute(const GDBusPropertyTable *propert, + DBusMessageIter *iter, + GDBusPendingPropertySet id, void *user_data) +{ + dbus_bool_t enable; + + dbus_message_iter_get_basic(iter, &enable); + DBG("%s : %d\n", __func__, enable); + micp_discover_mute_char(); + g_dbus_pending_property_success(id); +} + +static void property_micp_read_mute(const GDBusPropertyTable *propert, + DBusMessageIter *iter, + GDBusPendingPropertySet id, void *user_data) +{ + uint16_t handle; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) { + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InvalidArguments", + "Expected UINT16"); + return; + } + dbus_message_iter_get_basic(iter, &handle); + DBG("%s : %x\n", __func__, handle); + + mics_mute_char_read(handle); + g_dbus_pending_property_success(id); +} + +static void property_micp_write_mute(const GDBusPropertyTable *propert, + DBusMessageIter *iter, + GDBusPendingPropertySet id, void *user_data) +{ + uint16_t handle; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) { + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InvalidArguments", + "Expected UINT16"); + return; + } + dbus_message_iter_get_basic(iter, &handle); + DBG("%s : %x\n", __func__, handle); + + micp_char_write_value(handle); + g_dbus_pending_property_success(id); +} +#endif /*MICP_MICS_PTS_FLAG*/ + static gboolean property_get_pairable_timeout( const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) @@ -3886,6 +3966,13 @@ static const GDBusPropertyTable adapter_properties[] = { { "DiscoverableTimeout", "u", property_get_discoverable_timeout, property_set_discoverable_timeout }, { "Pairable", "b", property_get_pairable, property_set_pairable }, +#ifdef MICP_MICS_PTS_FLAG + { "mics", "b", NULL, property_set_mute_state }, + { "mics_state", "b", NULL, property_mute_enable_disable }, + { "micp_disc", "b", NULL, property_micp_discover_mute }, + { "micp_read_char", "q", NULL, property_micp_read_mute }, + { "micp_write_char", "q", NULL, property_micp_write_mute }, +#endif /*MICP_MICS_PTS_FLAG*/ { "PairableTimeout", "u", property_get_pairable_timeout, property_set_pairable_timeout }, { "Discovering", "b", property_get_discovering }, diff --git a/src/shared/micp.c b/src/shared/micp.c index 25ffa6940..c5b814d98 100644 --- a/src/shared/micp.c +++ b/src/shared/micp.c @@ -6,6 +6,10 @@ * Copyright (C) 2023 NXP Semiconductors. All rights reserved. * */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + #define _GNU_SOURCE #include <inttypes.h> #include <string.h> @@ -74,6 +78,11 @@ struct bt_micp { void *user_data; }; +#ifdef MICP_MICS_PTS_FLAG +struct bt_mics *pts_mics; +struct bt_micp *pts_micp; +#endif /*MICP_MICS_PTS_FLAG*/ + static struct queue *micp_db; static struct queue *micp_cbs; static struct queue *sessions; @@ -532,6 +541,9 @@ static struct bt_micp_db *micp_db_new(struct gatt_db *db) mdb->mics = mics_new(db); mdb->mics->mdb = mdb; +#ifdef MICP_MICS_PTS_FLAG + pts_mics = mdb->mics; +#endif /*MICP_MICS_PTS_FLAG*/ queue_push_tail(micp_db, mdb); return mdb; @@ -783,6 +795,10 @@ static void foreach_mics_char(struct gatt_db_attribute *attr, void *user_data) bt_uuid_t uuid, uuid_mute; struct bt_mics *mics; +#ifdef MICP_MICS_PTS_FLAG + pts_micp = micp; +#endif /*MICP_MICS_PTS_FLAG*/ + if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) return; @@ -920,3 +936,109 @@ bool bt_micp_attach(struct bt_micp *micp, struct bt_gatt_client *client) micp); return true; } + +#ifdef MICP_MICS_PTS_FLAG +void mics_change_mute_state(bool state) +{ + if (pts_micp == NULL) + return; + + DBG(pts_micp, "%s: %d", __func__, state); + state == true ? mics_muted(pts_mics, pts_micp, 0) : + mics_not_muted(pts_mics, pts_micp, 0); +} + +static uint8_t mics_mute_enable_disable(struct bt_mics *mics, uint8_t state) +{ + uint8_t *mute_state; + + mute_state = mdb_get_mute_state(mics->mdb); + + *mute_state = state; + + return 0; +} + +void mics_enable_disable_mute(bool state) +{ + state == true ? mics_mute_enable_disable(pts_mics, MICS_MUTED) : + mics_mute_enable_disable(pts_mics, MICS_DISABLED); +} + +static void micp_char_search_cb(bool success, uint8_t att_ecode, + struct bt_gatt_result *result, + void *user_data) +{ + DBG(pts_micp, "micp_char_search_cb"); + +} + +static void micp_foreach_mics_service(struct gatt_db_attribute *attr, + void *user_data) +{ + uint16_t start, end; + bool primary; + bt_uuid_t uuid; + struct bt_gatt_request *gatt_ret; + struct bt_att *micp_att; + struct bt_micp *micp = user_data; + struct bt_mics *mics = micp_get_mics(micp); + + if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary, + &uuid)) { + DBG(micp, "%s: ERR! gatt_db_attribute_get_service_data\n", + __func__); + return; + + } + micp_att = bt_micp_get_att(micp); + gatt_ret = bt_gatt_discover_characteristics(micp_att, start, end, + micp_char_search_cb, NULL, NULL); + + if (gatt_ret) + DBG(micp, "MICP GATT DISCOVER START\n"); + else + DBG(micp, "MICP GATT DISCOVER FAILED\n"); + + mics->service = attr; + + gatt_db_service_set_claimed(attr, true); + gatt_db_service_foreach_char(attr, foreach_mics_char, micp); +} + +void micp_discover_mute_char(void) +{ + bt_uuid_t uuid; + + bt_uuid16_create(&uuid, MICS_UUID); + gatt_db_foreach_service(pts_micp->ldb->db, &uuid, + micp_foreach_mics_service, pts_micp); +} + +void mics_mute_char_read(uint16_t handle) +{ + DBG(pts_micp, "%s. handle: %x\n", __func__, handle); + micp_read_value(pts_micp, handle, read_mute_state, pts_micp); +} + +static void micp_write_cb(bool success, uint8_t att_ecode, void *user_data) +{ + if (success) + DBG(pts_micp, "MICP Write successful\n"); + else + DBG(pts_micp, "\nWrite failed: 0x%02x\n", att_ecode); +} + +void micp_char_write_value(uint16_t handle) +{ + const uint8_t value = 0x01; + + if (!pts_micp->client) { + DBG(pts_micp, "%s: pts_micp->client is NULL", __func__); + return; + } + bt_gatt_client_write_value(pts_micp->client, handle, &value, 0x01, + micp_write_cb, NULL, NULL); + +} +#endif /*MICP_MICS_PTS_FLAG*/ diff --git a/src/shared/micp.h b/src/shared/micp.h index b307ac9f4..4a9807ed4 100644 --- a/src/shared/micp.h +++ b/src/shared/micp.h @@ -44,3 +44,11 @@ bool bt_micp_ready_unregister(struct bt_micp *micp, unsigned int id); bool bt_micp_unregister(unsigned int id); struct bt_micp *bt_micp_new(struct gatt_db *ldb, struct gatt_db *rdb); + +#ifdef MICP_MICS_PTS_FLAG +void mics_change_mute_state(bool state); +void mics_enable_disable_mute(bool state); +void micp_discover_mute_char(void); +void mics_mute_char_read(uint16_t handle); +void micp_char_write_value(uint16_t handle); +#endif /*MICP_MICS_PTS_FLAG*/ -- 2.34.1