Message ID | 20230905070912.82340-5-nitin.jadhav@nxp.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Implementation of MICS and MICP | expand |
Context | Check | Description |
---|---|---|
tedd_an/pre-ci_am | success | Success |
Hi Nitin, On Tue, Sep 5, 2023 at 12:09 AM Nitin Jadhav <nitin.jadhav@nxp.com> wrote: > > Implemented Unit Testcases for MICS[test-micp.c] and MICP[test-mics.c]. Let's have the server and client test on the same file in test-micp. > Co-developed-by: Mahesh Talewad <mahesh.talewad@nxp.com> > Signed-off-by: Mahesh Talewad <mahesh.talewad@nxp.com> > Signed-off-by: Nitin Jadhav <nitin.jadhav@nxp.com> > --- > Makefile.am | 13 ++ > unit/test-micp.c | 357 +++++++++++++++++++++++++++++++++++++++++++++++ > unit/test-mics.c | 317 +++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 687 insertions(+) > create mode 100644 unit/test-micp.c > create mode 100644 unit/test-mics.c I was expecting the test cases to work: unit/test-micp: MICS/SR/SPE/BI-02-C - init MICS/SR/SPE/BI-02-C - setup MICS/SR/SPE/BI-02-C - setup complete MICS/SR/SPE/BI-02-C - run ** ERROR:src/shared/tester.c:954:test_io_recv: assertion failed: (memcmp(buf, iov->iov_base, len) == 0) Bail out! ERROR:src/shared/tester.c:954:test_io_recv: assertion failed: (memcmp(buf, iov->iov_base, len) == 0) unit/test-mics just hangs so these seem to need some work. > diff --git a/Makefile.am b/Makefile.am > index 6f40f2a74..cde55bebf 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -573,6 +573,19 @@ unit_test_gattrib_LDADD = src/libshared-glib.la \ > lib/libbluetooth-internal.la \ > $(GLIB_LIBS) $(DBUS_LIBS) -ldl -lrt > > +unit_tests += unit/test-micp > + > +unit_test_micp_SOURCES = unit/test-micp.c > + > +unit_test_micp_LDADD = src/libshared-glib.la \ > + lib/libbluetooth-internal.la $(GLIB_LIBS) > + > +unit_tests += unit/test-mics > + > +unit_test_mics_SOURCES = unit/test-mics.c > +unit_test_mics_LDADD = src/libshared-glib.la \ > + lib/libbluetooth-internal.la $(GLIB_LIBS) > + > unit_tests += unit/test-bap > > unit_test_bap_SOURCES = unit/test-bap.c > diff --git a/unit/test-micp.c b/unit/test-micp.c > new file mode 100644 > index 000000000..3db32a4f7 > --- /dev/null > +++ b/unit/test-micp.c > @@ -0,0 +1,357 @@ > +// SPDX-License-Identifier: LGPL-2.1-or-later > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2023 NXP Semiconductors. All rights reserved. > + * > + */ > + > +#ifdef HAVE_CONFIG_H > +#include <config.h> > +#endif > + > +#define _GNU_SOURCE > +#include <unistd.h> > +#include <string.h> > +#include <sys/socket.h> > +#include <fcntl.h> > + > + > +#include <glib.h> > + > +#include "lib/bluetooth.h" > +#include "lib/uuid.h" > +#include "src/shared/util.h" > +#include "src/shared/tester.h" > +#include "src/shared/queue.h" > +#include "src/shared/att.h" > +#include "src/shared/gatt-db.h" > +#include "src/shared/gatt-server.h" > +#include "src/shared/micp.h" > + > +struct test_data { > + struct gatt_db *db; > + struct bt_micp *micp; > + struct bt_gatt_server *server; > + struct bt_gatt_client *client; > + struct queue *ccc_states; > + size_t iovcnt; > + struct iovec *iov; > + struct test_config *cfg; > +}; > + > +struct ccc_state { > + uint16_t handle; > + uint16_t value; > +}; > + > +struct notify { > + uint16_t handle, ccc_handle; > + uint8_t *value; > + uint16_t len; > + bt_gatt_server_conf_func_t conf; > + void *user_data; > +}; > + > +#define iov_data(args...) ((const struct iovec[]) { args }) > + > +#define define_test(name, function, _cfg, args...) \ > + do { \ > + const struct iovec iov[] = { args }; \ > + static struct test_data data; \ > + data.cfg = _cfg; \ > + data.iovcnt = ARRAY_SIZE(iov_data(args)); \ > + data.iov = util_iov_dup(iov, ARRAY_SIZE(iov_data(args))); \ > + tester_add(name, &data, NULL, function, \ > + test_teardown); \ > + } while (0) > + > +static void print_debug(const char *str, void *user_data) > +{ > + const char *prefix = user_data; > + > + if (tester_use_debug()) > + tester_debug("%s%s", prefix, str); > +} > + > +static void test_teardown(const void *user_data) > +{ > + struct test_data *data = (void *)user_data; > + > + bt_micp_unref(data->micp); > + bt_gatt_server_unref(data->server); > + util_iov_free(data->iov, data->iovcnt); > + gatt_db_unref(data->db); > + > + queue_destroy(data->ccc_states, free); > + > + tester_teardown_complete(); > +} > + > +static void test_complete_cb(const void *user_data) > +{ > + tester_test_passed(); > +} > + > +static bool ccc_state_match(const void *a, const void *b) > +{ > + const struct ccc_state *ccc = a; > + uint16_t handle = PTR_TO_UINT(b); > + > + return ccc->handle == handle; > +} > + > +static struct ccc_state *find_ccc_state(struct test_data *data, > + uint16_t handle) > +{ > + return queue_find(data->ccc_states, ccc_state_match, > + UINT_TO_PTR(handle)); > +} > + > +static struct ccc_state *get_ccc_state(struct test_data *data, uint16_t handle) > +{ > + struct ccc_state *ccc; > + > + ccc = find_ccc_state(data, handle); > + if (ccc) > + return ccc; > + > + ccc = new0(struct ccc_state, 1); > + ccc->handle = handle; > + queue_push_tail(data->ccc_states, ccc); > + > + return ccc; > +} > + > +static void gatt_notify_cb(struct gatt_db_attribute *attrib, > + struct gatt_db_attribute *ccc, > + const uint8_t *value, size_t len, > + struct bt_att *att, void *user_data) > +{ > + struct test_data *data = user_data; > + struct notify notify; > + > + memset(¬ify, 0, sizeof(notify)); > + > + notify.handle = gatt_db_attribute_get_handle(attrib); > + notify.ccc_handle = gatt_db_attribute_get_handle(ccc); > + notify.value = (void *) value; > + notify.len = len; > + > + printf("%s: notify.value:%d notify->len:%d\n", __func__, > + (int)*(notify.value), notify.len); > + if (!bt_gatt_server_send_notification(data->server, > + notify.handle, notify.value, > + notify.len, false)) > + printf("%s: Failed to send notification\n", __func__); > +} > + > +static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib, > + unsigned int id, uint16_t offset, > + uint8_t opcode, struct bt_att *att, > + void *user_data) > +{ > + struct test_data *data = user_data; > + struct ccc_state *ccc; > + uint16_t handle; > + uint8_t ecode = 0; > + const uint8_t *value = NULL; > + size_t len = 0; > + > + handle = gatt_db_attribute_get_handle(attrib); > + > + ccc = get_ccc_state(data, handle); > + if (!ccc) { > + ecode = BT_ATT_ERROR_UNLIKELY; > + goto done; > + } > + > + len = sizeof(ccc->value); > + value = (void *) &ccc->value; > + > +done: > + gatt_db_attribute_read_result(attrib, id, ecode, value, len); > +} > + > +static void test_server(const void *user_data) > +{ > + struct test_data *data = (void *)user_data; > + struct bt_att *att; > + struct io *io; > + > + io = tester_setup_io(data->iov, data->iovcnt); > + g_assert(io); > + > + tester_io_set_complete_func(test_complete_cb); > + > + att = bt_att_new(io_get_fd(io), false); > + g_assert(att); > + > + bt_att_set_debug(att, BT_ATT_DEBUG, print_debug, "bt_att:", NULL); > + > + data->db = gatt_db_new(); > + g_assert(data->db); > + > + gatt_db_ccc_register(data->db, gatt_ccc_read_cb, NULL, > + gatt_notify_cb, data); > + > + data->micp = bt_micp_new(data->db, NULL); > + g_assert(data->micp); > + > + data->server = bt_gatt_server_new(data->db, att, 64, 0); > + g_assert(data->server); > + > + bt_gatt_server_set_debug(data->server, print_debug, "bt_gatt_server:", > + NULL); > + > + data->ccc_states = queue_new(); > + > + tester_io_send(); > + > + bt_att_unref(att); > +} > + > +#define EXCHANGE_MTU IOV_DATA(0x02, 0x40, 0x00), \ > + IOV_DATA(0x03, 0x40, 0x00) > + > +#define MICS_MUTE_WRITE_VAL_00 \ > + IOV_DATA(0x12, 0x03, 0x00, 0x00), \ > + IOV_DATA(0x13) > + > +#define MICS_MUTE_WRITE_VAL_01 \ > + IOV_DATA(0x12, 0x03, 0x00, 0x01), \ > + IOV_DATA(0x13) > + > +#define MICS_MUTE_READ \ > + IOV_DATA(0x0a, 0x03, 0x00), \ > + IOV_DATA(0x0b, 0x01) > + > +#define DISCOVER_PRIM_SERV_NOTIF \ > + IOV_DATA(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ > + IOV_DATA(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x4d, 0x18), \ > + IOV_DATA(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \ > + IOV_DATA(0x01, 0x10, 0x05, 0x00, 0x0a) > + > +/* ATT: Read By Type Request (0x08) len 6 > + * Handle range: 0x0001-0x0009 > + * Attribute type: Characteristic (0x2803) > + * ATT: Read By Type Response (0x09) len 22 > + * Attribute data length: 7 > + * Handle: 0x0002 > + * Value: 1a0300c82b > + * Properties: 0x1a > + * Value Handle: 0x0003 > + * Value UUID: Mute (0x2bc3) > + */ > +#define DISC_MICS_CHAR_1 \ > + IOV_DATA(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28), \ > + IOV_DATA(0x09, 0x07, \ > + 0x02, 0x00, 0x1a, 0x03, 0x00, 0xc3, 0x2b), \ > + IOV_DATA(0x08, 0x05, 0x00, 0x05, 0x00, 0x03, 0x28), \ > + IOV_DATA(0x01, 0x08, 0x05, 0x00, 0x0a) > + > + > +#define MICS_FIND_BY_TYPE_VALUE \ > + IOV_DATA(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x4d, 0x18), \ > + IOV_DATA(0x07, 0x01, 0x00, 0x04, 0x00), \ > + IOV_DATA(0x06, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28, 0x4d, 0x18), \ > + IOV_DATA(0x01, 0x06, 0x05, 0x00, 0x0a) > + > +#define DISC_MICS_CHAR_AFTER_TYPE \ > + IOV_DATA(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28), \ > + IOV_DATA(0x09, 0x07, \ > + 0x02, 0x00, 0x1a, 0x03, 0x00, 0xc3, 0x2b), \ > + IOV_DATA(0x08, 0x03, 0x00, 0x05, 0x00, 0x03, 0x28), \ > + IOV_DATA(0x01, 0x08, 0x03, 0x00, 0x0a) > + > +#define MICS_WRITE_CCD \ > + IOV_DATA(0x12, 0x04, 0x00, 0x00, 0x00), \ > + IOV_DATA(0x13), \ > + IOV_DATA(0x12, 0x04, 0x00, 0x01, 0x00), \ > + IOV_DATA(0x13) > + > +#define MICS_FIND_INFO \ > + IOV_DATA(0x04, 0x04, 0x00, 0x05, 0x00), \ > + IOV_DATA(0x05, 0x01, 0x04, 0x00, 0x02, 0x29), \ > + IOV_DATA(0x04, 0x05, 0x00, 0x05, 0x00), \ > + IOV_DATA(0x01, 0x04, 0x05, 0x00, 0x0a) > + > +#define MICS_SR_SPN_BV_01_C \ > + EXCHANGE_MTU, \ > + DISCOVER_PRIM_SERV_NOTIF, \ > + DISC_MICS_CHAR_1, \ > + MICS_FIND_BY_TYPE_VALUE, \ > + DISC_MICS_CHAR_AFTER_TYPE, \ > + MICS_FIND_INFO, \ > + MICS_WRITE_CCD, \ > + IOV_DATA(0x0a, 0x03, 0x00), \ > + IOV_DATA(0x0b, 0x01), \ > + MICS_MUTE_WRITE_VAL_00, \ > + IOV_DATA(0x1b, 0x03, 0x00, 0x00), \ > + MICS_MUTE_WRITE_VAL_01, \ > + IOV_DATA(0x1b, 0x03, 0x00, 0x01), \ > + IOV_DATA(0x0a, 0x03, 0x00), \ > + IOV_DATA(0x0b, 0x01) > + > +#define MICS_SR_SGGIT_SER_BV_01_C \ > + EXCHANGE_MTU, \ > + DISCOVER_PRIM_SERV_NOTIF, \ > + MICS_FIND_BY_TYPE_VALUE > + > +#define MICS_SR_SGGIT_CHA_BV_01_C \ > + EXCHANGE_MTU, \ > + DISCOVER_PRIM_SERV_NOTIF, \ > + MICS_FIND_BY_TYPE_VALUE, \ > + DISC_MICS_CHAR_AFTER_TYPE > + > +#define MICS_WRITE_MUTE_CHAR_INVALID \ > + IOV_DATA(0x12, 0x03, 0x00, 0x02), \ > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x13), \ > + IOV_DATA(0x12, 0x03, 0x00, 0x05), \ > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x13) > + > +#define MICS_SR_SPE_BI_1_C \ > + EXCHANGE_MTU, \ > + DISCOVER_PRIM_SERV_NOTIF, \ > + MICS_FIND_BY_TYPE_VALUE, \ > + MICS_WRITE_MUTE_CHAR_INVALID > + > +#define MICS_MUTE_READ_INVALID \ > + IOV_DATA(0x0a, 0x03, 0x00), \ > + IOV_DATA(0x0b, 0x02) > + > +#define MICS_MUTE_WRITE_1 \ > + IOV_DATA(0x12, 0x03, 0x00, 0x01), \ > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x80) > + > +#define MICS_MUTE_WRITE_0 \ > + IOV_DATA(0x12, 0x03, 0x00, 0x00), \ > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x80) > + > +#define MICS_SR_SPE_BI_02_C \ > + EXCHANGE_MTU, \ > + DISCOVER_PRIM_SERV_NOTIF, \ > + MICS_FIND_BY_TYPE_VALUE, \ > + MICS_MUTE_READ_INVALID, \ > + MICS_MUTE_WRITE_0, \ > + MICS_MUTE_WRITE_1 Please have the PDU definition with a proper description e.g. use btmon to collect them while running e.g. sudo unit/test-micp -m packets are forwarded to btmon. Also please be consistent with the tab alignment, sometimes it is just one tab sometimes it is many. > +int main(int argc, char *argv[]) > +{ > + > + tester_init(&argc, &argv); > + > + define_test("MICS/SR/SGGIT/SER/BV-01-C", test_server, NULL, > + MICS_SR_SGGIT_SER_BV_01_C); > + define_test("MICS/SR/SGGIT/CHA/BV-01-C", test_server, NULL, > + MICS_SR_SGGIT_CHA_BV_01_C); > + define_test("MICS/SR/SPE/BI-01-C", test_server, NULL, > + MICS_SR_SPE_BI_1_C); > + define_test("MICS/SR/SPE/BI-02-C", test_server, NULL, > + MICS_SR_SPE_BI_02_C); > + define_test("MICS/SR/SPN/BV-01-C", test_server, NULL, > + MICS_SR_SPN_BV_01_C); > + > + return tester_run(); > +} > diff --git a/unit/test-mics.c b/unit/test-mics.c > new file mode 100644 > index 000000000..7a7d70bf3 > --- /dev/null > +++ b/unit/test-mics.c > @@ -0,0 +1,317 @@ > +// SPDX-License-Identifier: LGPL-2.1-or-later > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2023 NXP Semiconductors. All rights reserved. > + * > + */ > + > +#ifdef HAVE_CONFIG_H > +#include <config.h> > +#endif > + > +#define _GNU_SOURCE > +#include <unistd.h> > +#include <string.h> > +#include <sys/socket.h> > +#include <fcntl.h> > + > + > +#include <glib.h> > + > +#include "lib/bluetooth.h" > +#include "lib/uuid.h" > +#include "btio/btio.h" > +#include "src/shared/util.h" > +#include "src/shared/tester.h" > +#include "src/shared/queue.h" > +#include "src/shared/att.h" > +#include "src/shared/gatt-db.h" > +#include "src/shared/gatt-helpers.h" > +#include "src/shared/micp.h" > + > +struct test_data { > + struct gatt_db *db; > + struct bt_mics *mics; > + struct bt_micp *micp; > + struct bt_gatt_client *client; > + size_t iovcnt; > + struct iovec *iov; > + struct test_config *cfg; > +}; > + > +struct db_attribute_micp_test_data { > + struct gatt_db_attribute *match; > + bool found; > +}; > + > +#define MICP_GATT_CLIENT_MTU 64 > +#define iov_data(args...) ((const struct iovec[]) { args }) > + > +#define define_test(name, function, _cfg, args...) \ > + do { \ > + const struct iovec iov[] = { args }; \ > + static struct test_data data; \ > + data.cfg = _cfg; \ > + data.iovcnt = ARRAY_SIZE(iov_data(args)); \ > + data.iov = util_iov_dup(iov, ARRAY_SIZE(iov_data(args))); \ > + tester_add(name, &data, test_setup, function, \ > + test_teardown); \ > + } while (0) > + > +static void print_debug(const char *str, void *user_data) > +{ > + const char *prefix = user_data; > + > + if (tester_use_debug()) > + tester_debug("%s %s", prefix, str); > +} > + > +static void test_teardown(const void *user_data) > +{ > + struct test_data *data = (void *)user_data; > + > + bt_gatt_client_unref(data->client); > + util_iov_free(data->iov, data->iovcnt); > + gatt_db_unref(data->db); > + > + tester_teardown_complete(); > +} > + > +static void test_complete_cb(const void *user_data) > +{ > + tester_test_passed(); > +} > + > +static void client_ready_cb(bool success, uint8_t att_ecode, void *user_data) > +{ > + > + if (!success) > + tester_setup_failed(); > + else > + tester_setup_complete(); > +} > + > +static void micp_write_cb(bool success, uint8_t att_ecode, void *user_data) > +{ > + if (success) > + printf("MICP Write successful\n"); > + else > + printf("\nWrite failed: 0x%02x\n", att_ecode); > +} > + > +static void micp_write_value(struct bt_micp *micp, void *user_data) > +{ > + struct bt_mics *mics = micp_get_mics(micp); > + uint16_t value_handle; > + int ret; > + const uint16_t value = 0x0001; > + > + gatt_db_attribute_get_char_data(mics->ms, NULL, &value_handle, > + NULL, NULL, NULL); > + > + printf("%s handle: %x\n", __func__, value_handle); > + ret = bt_gatt_client_write_value(micp->client, value_handle, > + (void *)&value, sizeof(value), micp_write_cb, NULL, NULL); > + > + if (!ret) > + printf("bt_gatt_client_write_value() : Write FAILED"); > +} > + > +static void micp_ready(struct bt_micp *micp, void *user_data) > +{ > + micp_write_value(micp, user_data); > +} > + > +static void test_client(const void *user_data) > +{ > + struct test_data *data = (void *)user_data; > + struct io *io; > + > + io = tester_setup_io(data->iov, data->iovcnt); > + g_assert(io); > + > + tester_io_set_complete_func(test_complete_cb); > + > + data->db = gatt_db_new(); > + g_assert(data->db); > + > + data->micp = bt_micp_new(data->db, bt_gatt_client_get_db(data->client)); > + g_assert(data->micp); > + > + bt_micp_set_debug(data->micp, print_debug, "bt_mip: ", NULL); > + > + bt_micp_ready_register(data->micp, micp_ready, data, NULL); > + > + bt_micp_attach(data->micp, data->client); > +} > + > + /* ATT: Exchange MTU Response (0x03) len 2 > + * Server RX MTU: 64 > + */ > + /* ATT: Exchange MTU Request (0x02) len 2 > + * Client RX MTU: 64 > + */ > +#define ATT_EXCHANGE_MTU IOV_DATA(0x02, 0x40, 0x00), \ > + IOV_DATA(0x03, 0x40, 0x00) > + > +/* > + * ATT: Read By Type Request (0x08) len 6 > + * Handle range: 0x0001-0xffff > + * Attribute type: Server Supported Features (0x2b3a) > + */ > +#define MICP_READ_SR_FEATURE IOV_DATA(0x08, 0x01, 0x00, 0Xff, 0xff, \ > + 0x3a, 0x2b), \ > + IOV_DATA(0x01, 0x08, 0x01, 0x00, 0x0a) > + > + /* > + * ATT: Read By Group Type Request (0x10) len 6 > + * Handle range: 0x0001-0xffff > + * Attribute group type: Primary Service (0x2800) > + */ > + > +/* > + * ATT: Read By Group Type Response (0x11) len 7 > + * Attribute data length: 6 > + * Attribute group list: 1 entry > + * Handle range: 0x00a0-0x00a4 > + * UUID: Microphone Control (0x184d) > + */ > +#define MICP_READ_GROUP_TYPE \ > + IOV_DATA(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ > + IOV_DATA(0x11, 0x06, \ > + 0x01, 0x00, 0x04, 0x00, 0x4d, 0x18), \ > + IOV_DATA(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \ > + IOV_DATA(0x01, 0x10, 0x06, 0x00, 0x0a) > + > + /* ATT: Read By Group Type Request (0x10) len 6 > + * Handle range: 0x0001-0xffff > + * Attribute group type: Secondary Service (0x2801) > + */ > + /* ATT: Error Response (0x01) len 4 > + * Read By Group Type Request (0x10) > + * Handle: 0x0001 > + * Error: Attribute Not Found (0x0a)08 01 00 05 00 02 28 > + */ > +#define MICP_READ_REQ_SECOND_SERVICE \ > + IOV_DATA(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \ > + IOV_DATA(0x01, 0x10, 0x01, 0x00, 0x0a) > + > +#define MICP_READ_REQ_INCLUDE_SERVICE \ > + IOV_DATA(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28), \ > + IOV_DATA(0x01, 0x08, 0x01, 0x00, 0x0a) > + > + /* ATT: Read By Type Request (0x08) len 6 > + * Handle range: 0x0001-0x0004 > + * Attribute type: Characteristic (0x2803) > + */ > + > +/* ATT: Find Information Request (0x04) len 4 > + * Handle range: 0x0004-0x0004 > + */ > +#define MICP_FIND_INFO_REQ \ > + IOV_DATA(0x04, 0x04, 0x00, 0x04, 0x00), \ > + IOV_DATA(0x05, 0x01, 0x04, 0x00, 0x02, 0x29) > + > +/* > + * ATT: Read By Type Request (0x08) len 6 > + * Handle range: 0x0001-0x0004 > + * Attribute type: Characteristic (0x2803) > + */ > +#define MICP_READ_REQ_CHAR \ > + IOV_DATA(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),\ > + IOV_DATA(0x09, 0x07, \ > + 0x02, 0x00, 0x1a, 0x03, 0x00, 0xc3, 0x2b), \ > + IOV_DATA(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28), \ > + IOV_DATA(0x01, 0x08, 0x04, 0x00, 0x0a) > + > +#define MICS_MUTE_READ \ > + IOV_DATA(0x0a, 0x03, 0x00), \ > + IOV_DATA(0x0b, 0x01) > + > +#define MICS_EN_MUTE_DISCPTR \ > + IOV_DATA(0x12, 0x04, 0x00, 0x01, 0x00), \ > + IOV_DATA(0x13) > + > +#define MICS_MUTE_WRITE \ > + IOV_DATA(0x12, 0x03, 0x00, 0x01),\ > + IOV_DATA(0x13) > + > +#define MICP_CL_CGGIT_SER_BV_01_C \ > + MICS_MUTE_READ, \ > + MICS_EN_MUTE_DISCPTR, \ > + IOV_DATA(0x12, 0x03, 0x00, 0x01, 0x00), \ > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x013) > + > +#define MICP_CL_CGGIT_CHA_BV_01_C \ > + MICS_MUTE_READ, \ > + MICS_EN_MUTE_DISCPTR, \ > + IOV_DATA(0x12, 0x03, 0x00, 0x06, 0x00), \ > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x013), \ > + MICS_MUTE_READ > + > +#define MICP_CL_SPE_BI_01_C \ > + MICS_MUTE_READ, \ > + MICS_EN_MUTE_DISCPTR, \ > + IOV_DATA(0x12, 0x03, 0x00, 0x01, 0x00), \ > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x80) > + > +/* GATT Discover All procedure */ > +static const struct iovec setup_data[] = { > + ATT_EXCHANGE_MTU, > + MICP_READ_SR_FEATURE, > + MICP_READ_GROUP_TYPE, > + MICP_READ_REQ_SECOND_SERVICE, > + MICP_READ_REQ_INCLUDE_SERVICE, > + MICP_READ_REQ_CHAR, > + MICP_FIND_INFO_REQ > +}; > + > +static void test_setup(const void *user_data) > +{ > + struct test_data *data = (void *)user_data; > + struct bt_att *att; > + struct gatt_db *db; > + struct io *io; > + > + io = tester_setup_io(setup_data, ARRAY_SIZE(setup_data)); > + g_assert(io); > + > + att = bt_att_new(io_get_fd(io), false); > + g_assert(att); > + > + bt_att_set_debug(att, BT_ATT_DEBUG, print_debug, "bt_att:", NULL); > + > + db = gatt_db_new(); > + g_assert(db); > + > + > + data->client = bt_gatt_client_new(db, att, MICP_GATT_CLIENT_MTU, 0); > + g_assert(data->client); > + > + bt_gatt_client_set_debug(data->client, print_debug, "bt_gatt_client:", > + NULL); > + > + bt_gatt_client_ready_register(data->client, client_ready_cb, data, > + NULL); > + > + bt_att_unref(att); > + gatt_db_unref(db); > +} > + > + > +int main(int argc, char *argv[]) > +{ > + > + tester_init(&argc, &argv); > + > + define_test("MICP/CL/CGGIT/SER/BV-01-C", test_client, NULL, > + MICS_MUTE_READ); > + define_test("MICP/CL/CGGIT/CHA/BV-01-C", test_client, NULL, > + MICP_CL_CGGIT_SER_BV_01_C); > + define_test("MICP/CL/SPE/BI-01-C", test_client, NULL, > + MICP_CL_SPE_BI_01_C); > + > + return tester_run(); > +} > -- > 2.34.1 >
Hello Luiz, Please refer the inline comments. Thanks, Warm Regards Nitin Jadhav > -----Original Message----- > From: Luiz Augusto von Dentz <luiz.dentz@gmail.com> > Sent: Tuesday, September 5, 2023 11:55 PM > To: Nitin Jadhav <nitin.jadhav@nxp.com> > Cc: linux-bluetooth@vger.kernel.org; Devyani Godbole > <devyani.godbole@nxp.com>; Mahesh Talewad > <mahesh.talewad@nxp.com> > Subject:Re: [PATCH BlueZ v2 4/4] unit/test-micp-test-mics: To > implement unit tester code > > > > Hi Nitin, > > On Tue, Sep 5, 2023 at 12:09 AM Nitin Jadhav <nitin.jadhav@nxp.com> wrote: > > > > Implemented Unit Testcases for MICS[test-micp.c] and MICP[test-mics.c]. > > Let's have the server and client test on the same file in test-micp. > We kept this separate considering to test Profile and Service separately. Noted your suggestion we will try to have it in same file. > > > Co-developed-by: Mahesh Talewad <mahesh.talewad@nxp.com> > > Signed-off-by: Mahesh Talewad <mahesh.talewad@nxp.com> > > Signed-off-by: Nitin Jadhav <nitin.jadhav@nxp.com> > > --- > > Makefile.am | 13 ++ > > unit/test-micp.c | 357 > > +++++++++++++++++++++++++++++++++++++++++++++++ > > unit/test-mics.c | 317 +++++++++++++++++++++++++++++++++++++++++ > > 3 files changed, 687 insertions(+) > > create mode 100644 unit/test-micp.c > > create mode 100644 unit/test-mics.c > > I was expecting the test cases to work: > > unit/test-micp: > > MICS/SR/SPE/BI-02-C - init > MICS/SR/SPE/BI-02-C - setup > MICS/SR/SPE/BI-02-C - setup complete > MICS/SR/SPE/BI-02-C - run > ** > ERROR:src/shared/tester.c:954:test_io_recv: assertion failed: > (memcmp(buf, iov->iov_base, len) == 0) > Bail out! ERROR:src/shared/tester.c:954:test_io_recv: assertion > failed: (memcmp(buf, iov->iov_base, len) == 0) > > unit/test-mics just hangs so these seem to need some work. > In function static struct bt_mics *mics_new(struct gatt_db *db) by default the mics->mute_stat is set to MICS_MUTED. As per test specs, MICS/SR/SPE/BI-02-C -> Initial condition: The IUT has set its Mute state to Disabled (0x02). To verify this Unit test case we have to modify the initial state of mics->mute_stat to MICS_DISABLED in code then it works fine. > > > diff --git a/Makefile.am b/Makefile.am index 6f40f2a74..cde55bebf > > 100644 > > --- a/Makefile.am > > +++ b/Makefile.am > > @@ -573,6 +573,19 @@ unit_test_gattrib_LDADD = src/libshared-glib.la \ > > lib/libbluetooth-internal.la \ > > $(GLIB_LIBS) $(DBUS_LIBS) -ldl -lrt > > > > +unit_tests += unit/test-micp > > + > > +unit_test_micp_SOURCES = unit/test-micp.c > > + > > +unit_test_micp_LDADD = src/libshared-glib.la \ > > + lib/libbluetooth-internal.la $(GLIB_LIBS) > > + > > +unit_tests += unit/test-mics > > + > > +unit_test_mics_SOURCES = unit/test-mics.c unit_test_mics_LDADD = > > +src/libshared-glib.la \ > > + lib/libbluetooth-internal.la > > +$(GLIB_LIBS) > > + > > unit_tests += unit/test-bap > > > > unit_test_bap_SOURCES = unit/test-bap.c diff --git a/unit/test-micp.c > > b/unit/test-micp.c new file mode 100644 index 000000000..3db32a4f7 > > --- /dev/null > > +++ b/unit/test-micp.c > > @@ -0,0 +1,357 @@ > > +// SPDX-License-Identifier: LGPL-2.1-or-later > > +/* > > + * > > + * BlueZ - Bluetooth protocol stack for Linux > > + * > > + * Copyright (C) 2023 NXP Semiconductors. All rights reserved. > > + * > > + */ > > + > > +#ifdef HAVE_CONFIG_H > > +#include <config.h> > > +#endif > > + > > +#define _GNU_SOURCE > > +#include <unistd.h> > > +#include <string.h> > > +#include <sys/socket.h> > > +#include <fcntl.h> > > + > > + > > +#include <glib.h> > > + > > +#include "lib/bluetooth.h" > > +#include "lib/uuid.h" > > +#include "src/shared/util.h" > > +#include "src/shared/tester.h" > > +#include "src/shared/queue.h" > > +#include "src/shared/att.h" > > +#include "src/shared/gatt-db.h" > > +#include "src/shared/gatt-server.h" > > +#include "src/shared/micp.h" > > + > > +struct test_data { > > + struct gatt_db *db; > > + struct bt_micp *micp; > > + struct bt_gatt_server *server; > > + struct bt_gatt_client *client; > > + struct queue *ccc_states; > > + size_t iovcnt; > > + struct iovec *iov; > > + struct test_config *cfg; > > +}; > > + > > +struct ccc_state { > > + uint16_t handle; > > + uint16_t value; > > +}; > > + > > +struct notify { > > + uint16_t handle, ccc_handle; > > + uint8_t *value; > > + uint16_t len; > > + bt_gatt_server_conf_func_t conf; > > + void *user_data; > > +}; > > + > > +#define iov_data(args...) ((const struct iovec[]) { args }) > > + > > +#define define_test(name, function, _cfg, args...) \ > > + do { \ > > + const struct iovec iov[] = { args }; \ > > + static struct test_data data; \ > > + data.cfg = _cfg; \ > > + data.iovcnt = ARRAY_SIZE(iov_data(args)); \ > > + data.iov = util_iov_dup(iov, ARRAY_SIZE(iov_data(args))); \ > > + tester_add(name, &data, NULL, function, \ > > + test_teardown); \ > > + } while (0) > > + > > +static void print_debug(const char *str, void *user_data) { > > + const char *prefix = user_data; > > + > > + if (tester_use_debug()) > > + tester_debug("%s%s", prefix, str); } > > + > > +static void test_teardown(const void *user_data) { > > + struct test_data *data = (void *)user_data; > > + > > + bt_micp_unref(data->micp); > > + bt_gatt_server_unref(data->server); > > + util_iov_free(data->iov, data->iovcnt); > > + gatt_db_unref(data->db); > > + > > + queue_destroy(data->ccc_states, free); > > + > > + tester_teardown_complete(); > > +} > > + > > +static void test_complete_cb(const void *user_data) { > > + tester_test_passed(); > > +} > > + > > +static bool ccc_state_match(const void *a, const void *b) { > > + const struct ccc_state *ccc = a; > > + uint16_t handle = PTR_TO_UINT(b); > > + > > + return ccc->handle == handle; > > +} > > + > > +static struct ccc_state *find_ccc_state(struct test_data *data, > > + uint16_t handle) { > > + return queue_find(data->ccc_states, ccc_state_match, > > + UINT_TO_PTR(handle)); } > > + > > +static struct ccc_state *get_ccc_state(struct test_data *data, > > +uint16_t handle) { > > + struct ccc_state *ccc; > > + > > + ccc = find_ccc_state(data, handle); > > + if (ccc) > > + return ccc; > > + > > + ccc = new0(struct ccc_state, 1); > > + ccc->handle = handle; > > + queue_push_tail(data->ccc_states, ccc); > > + > > + return ccc; > > +} > > + > > +static void gatt_notify_cb(struct gatt_db_attribute *attrib, > > + struct gatt_db_attribute *ccc, > > + const uint8_t *value, size_t len, > > + struct bt_att *att, void > > +*user_data) { > > + struct test_data *data = user_data; > > + struct notify notify; > > + > > + memset(¬ify, 0, sizeof(notify)); > > + > > + notify.handle = gatt_db_attribute_get_handle(attrib); > > + notify.ccc_handle = gatt_db_attribute_get_handle(ccc); > > + notify.value = (void *) value; > > + notify.len = len; > > + > > + printf("%s: notify.value:%d notify->len:%d\n", __func__, > > + (int)*(notify.value), notify.len); > > + if (!bt_gatt_server_send_notification(data->server, > > + notify.handle, notify.value, > > + notify.len, false)) > > + printf("%s: Failed to send notification\n", __func__); > > +} > > + > > +static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib, > > + unsigned int id, uint16_t offset, > > + uint8_t opcode, struct bt_att *att, > > + void *user_data) { > > + struct test_data *data = user_data; > > + struct ccc_state *ccc; > > + uint16_t handle; > > + uint8_t ecode = 0; > > + const uint8_t *value = NULL; > > + size_t len = 0; > > + > > + handle = gatt_db_attribute_get_handle(attrib); > > + > > + ccc = get_ccc_state(data, handle); > > + if (!ccc) { > > + ecode = BT_ATT_ERROR_UNLIKELY; > > + goto done; > > + } > > + > > + len = sizeof(ccc->value); > > + value = (void *) &ccc->value; > > + > > +done: > > + gatt_db_attribute_read_result(attrib, id, ecode, value, len); > > +} > > + > > +static void test_server(const void *user_data) { > > + struct test_data *data = (void *)user_data; > > + struct bt_att *att; > > + struct io *io; > > + > > + io = tester_setup_io(data->iov, data->iovcnt); > > + g_assert(io); > > + > > + tester_io_set_complete_func(test_complete_cb); > > + > > + att = bt_att_new(io_get_fd(io), false); > > + g_assert(att); > > + > > + bt_att_set_debug(att, BT_ATT_DEBUG, print_debug, "bt_att:", > > + NULL); > > + > > + data->db = gatt_db_new(); > > + g_assert(data->db); > > + > > + gatt_db_ccc_register(data->db, gatt_ccc_read_cb, NULL, > > + gatt_notify_cb, data); > > + > > + data->micp = bt_micp_new(data->db, NULL); > > + g_assert(data->micp); > > + > > + data->server = bt_gatt_server_new(data->db, att, 64, 0); > > + g_assert(data->server); > > + > > + bt_gatt_server_set_debug(data->server, print_debug, > "bt_gatt_server:", > > + NULL); > > + > > + data->ccc_states = queue_new(); > > + > > + tester_io_send(); > > + > > + bt_att_unref(att); > > +} > > + > > +#define EXCHANGE_MTU IOV_DATA(0x02, 0x40, 0x00), \ > > + IOV_DATA(0x03, 0x40, > > +0x00) > > + > > +#define MICS_MUTE_WRITE_VAL_00 \ > > + IOV_DATA(0x12, 0x03, 0x00, 0x00), \ > > + IOV_DATA(0x13) > > + > > +#define MICS_MUTE_WRITE_VAL_01 \ > > + IOV_DATA(0x12, 0x03, 0x00, 0x01), \ > > + IOV_DATA(0x13) > > + > > +#define MICS_MUTE_READ \ > > + IOV_DATA(0x0a, 0x03, 0x00), \ > > + IOV_DATA(0x0b, 0x01) > > + > > +#define DISCOVER_PRIM_SERV_NOTIF \ > > + IOV_DATA(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ > > + IOV_DATA(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x4d, 0x18), \ > > + IOV_DATA(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \ > > + IOV_DATA(0x01, 0x10, 0x05, 0x00, 0x0a) > > + > > +/* ATT: Read By Type Request (0x08) len 6 > > + * Handle range: 0x0001-0x0009 > > + * Attribute type: Characteristic (0x2803) > > + * ATT: Read By Type Response (0x09) len 22 > > + * Attribute data length: 7 > > + * Handle: 0x0002 > > + * Value: 1a0300c82b > > + * Properties: 0x1a > > + * Value Handle: 0x0003 > > + * Value UUID: Mute (0x2bc3) > > + */ > > +#define DISC_MICS_CHAR_1 \ > > + IOV_DATA(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28), \ > > + IOV_DATA(0x09, 0x07, \ > > + 0x02, 0x00, 0x1a, 0x03, 0x00, 0xc3, 0x2b), \ > > + IOV_DATA(0x08, 0x05, 0x00, 0x05, 0x00, 0x03, 0x28), \ > > + IOV_DATA(0x01, 0x08, 0x05, 0x00, 0x0a) > > + > > + > > +#define MICS_FIND_BY_TYPE_VALUE \ > > + IOV_DATA(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x4d, 0x18), \ > > + IOV_DATA(0x07, 0x01, 0x00, 0x04, 0x00), \ > > + IOV_DATA(0x06, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28, 0x4d, 0x18), \ > > + IOV_DATA(0x01, 0x06, 0x05, 0x00, 0x0a) > > + > > +#define DISC_MICS_CHAR_AFTER_TYPE \ > > + IOV_DATA(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28), \ > > + IOV_DATA(0x09, 0x07, \ > > + 0x02, 0x00, 0x1a, 0x03, 0x00, 0xc3, 0x2b), \ > > + IOV_DATA(0x08, 0x03, 0x00, 0x05, 0x00, 0x03, 0x28), \ > > + IOV_DATA(0x01, 0x08, 0x03, 0x00, 0x0a) > > + > > +#define MICS_WRITE_CCD \ > > + IOV_DATA(0x12, 0x04, 0x00, 0x00, 0x00), \ > > + IOV_DATA(0x13), \ > > + IOV_DATA(0x12, 0x04, 0x00, 0x01, 0x00), \ > > + IOV_DATA(0x13) > > + > > +#define MICS_FIND_INFO \ > > + IOV_DATA(0x04, 0x04, 0x00, 0x05, 0x00), \ > > + IOV_DATA(0x05, 0x01, 0x04, 0x00, 0x02, 0x29), \ > > + IOV_DATA(0x04, 0x05, 0x00, 0x05, 0x00), \ > > + IOV_DATA(0x01, 0x04, 0x05, 0x00, 0x0a) > > + > > +#define MICS_SR_SPN_BV_01_C \ > > + EXCHANGE_MTU, \ > > + DISCOVER_PRIM_SERV_NOTIF, \ > > + DISC_MICS_CHAR_1, \ > > + MICS_FIND_BY_TYPE_VALUE, \ > > + DISC_MICS_CHAR_AFTER_TYPE, \ > > + MICS_FIND_INFO, \ > > + MICS_WRITE_CCD, \ > > + IOV_DATA(0x0a, 0x03, 0x00), \ > > + IOV_DATA(0x0b, 0x01), \ > > + MICS_MUTE_WRITE_VAL_00, \ > > + IOV_DATA(0x1b, 0x03, 0x00, 0x00), \ > > + MICS_MUTE_WRITE_VAL_01, \ > > + IOV_DATA(0x1b, 0x03, 0x00, 0x01), \ > > + IOV_DATA(0x0a, 0x03, 0x00), \ > > + IOV_DATA(0x0b, 0x01) > > + > > +#define MICS_SR_SGGIT_SER_BV_01_C \ > > + EXCHANGE_MTU, \ > > + DISCOVER_PRIM_SERV_NOTIF, \ > > + > > +MICS_FIND_BY_TYPE_VALUE > > + > > +#define MICS_SR_SGGIT_CHA_BV_01_C \ > > + EXCHANGE_MTU, \ > > + DISCOVER_PRIM_SERV_NOTIF, \ > > + MICS_FIND_BY_TYPE_VALUE, \ > > + > > +DISC_MICS_CHAR_AFTER_TYPE > > + > > +#define MICS_WRITE_MUTE_CHAR_INVALID \ > > + IOV_DATA(0x12, 0x03, 0x00, 0x02), \ > > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x13), \ > > + IOV_DATA(0x12, 0x03, 0x00, 0x05), \ > > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x13) > > + > > +#define MICS_SR_SPE_BI_1_C \ > > + EXCHANGE_MTU, \ > > + DISCOVER_PRIM_SERV_NOTIF, \ > > + MICS_FIND_BY_TYPE_VALUE, \ > > + > > +MICS_WRITE_MUTE_CHAR_INVALID > > + > > +#define MICS_MUTE_READ_INVALID \ > > + IOV_DATA(0x0a, 0x03, 0x00), \ > > + IOV_DATA(0x0b, 0x02) > > + > > +#define MICS_MUTE_WRITE_1 \ > > + IOV_DATA(0x12, 0x03, 0x00, 0x01), \ > > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x80) > > + > > +#define MICS_MUTE_WRITE_0 \ > > + IOV_DATA(0x12, 0x03, 0x00, 0x00), \ > > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x80) > > + > > +#define MICS_SR_SPE_BI_02_C \ > > + EXCHANGE_MTU, \ > > + DISCOVER_PRIM_SERV_NOTIF, \ > > + MICS_FIND_BY_TYPE_VALUE, \ > > + MICS_MUTE_READ_INVALID, \ > > + MICS_MUTE_WRITE_0, \ > > + MICS_MUTE_WRITE_1 > > Please have the PDU definition with a proper description e.g. use btmon to > collect them while running e.g. sudo unit/test-micp -m packets are forwarded > to btmon. > > Also please be consistent with the tab alignment, sometimes it is just one tab > sometimes it is many. > Sure, we take care of this in next patch. > > > +int main(int argc, char *argv[]) > > +{ > > + > > + tester_init(&argc, &argv); > > + > > + define_test("MICS/SR/SGGIT/SER/BV-01-C", test_server, NULL, > > + MICS_SR_SGGIT_SER_BV_01_C); > > + define_test("MICS/SR/SGGIT/CHA/BV-01-C", test_server, NULL, > > + MICS_SR_SGGIT_CHA_BV_01_C); > > + define_test("MICS/SR/SPE/BI-01-C", test_server, NULL, > > + MICS_SR_SPE_BI_1_C); > > + define_test("MICS/SR/SPE/BI-02-C", test_server, NULL, > > + MICS_SR_SPE_BI_02_C); > > + define_test("MICS/SR/SPN/BV-01-C", test_server, NULL, > > + MICS_SR_SPN_BV_01_C); > > + > > + return tester_run(); > > +} > > diff --git a/unit/test-mics.c b/unit/test-mics.c new file mode 100644 > > index 000000000..7a7d70bf3 > > --- /dev/null > > +++ b/unit/test-mics.c > > @@ -0,0 +1,317 @@ > > +// SPDX-License-Identifier: LGPL-2.1-or-later > > +/* > > + * > > + * BlueZ - Bluetooth protocol stack for Linux > > + * > > + * Copyright (C) 2023 NXP Semiconductors. All rights reserved. > > + * > > + */ > > + > > +#ifdef HAVE_CONFIG_H > > +#include <config.h> > > +#endif > > + > > +#define _GNU_SOURCE > > +#include <unistd.h> > > +#include <string.h> > > +#include <sys/socket.h> > > +#include <fcntl.h> > > + > > + > > +#include <glib.h> > > + > > +#include "lib/bluetooth.h" > > +#include "lib/uuid.h" > > +#include "btio/btio.h" > > +#include "src/shared/util.h" > > +#include "src/shared/tester.h" > > +#include "src/shared/queue.h" > > +#include "src/shared/att.h" > > +#include "src/shared/gatt-db.h" > > +#include "src/shared/gatt-helpers.h" > > +#include "src/shared/micp.h" > > + > > +struct test_data { > > + struct gatt_db *db; > > + struct bt_mics *mics; > > + struct bt_micp *micp; > > + struct bt_gatt_client *client; > > + size_t iovcnt; > > + struct iovec *iov; > > + struct test_config *cfg; > > +}; > > + > > +struct db_attribute_micp_test_data { > > + struct gatt_db_attribute *match; > > + bool found; > > +}; > > + > > +#define MICP_GATT_CLIENT_MTU 64 > > +#define iov_data(args...) ((const struct iovec[]) { args }) > > + > > +#define define_test(name, function, _cfg, args...) \ > > + do { \ > > + const struct iovec iov[] = { args }; \ > > + static struct test_data data; \ > > + data.cfg = _cfg; \ > > + data.iovcnt = ARRAY_SIZE(iov_data(args)); \ > > + data.iov = util_iov_dup(iov, ARRAY_SIZE(iov_data(args))); \ > > + tester_add(name, &data, test_setup, function, \ > > + test_teardown); \ > > + } while (0) > > + > > +static void print_debug(const char *str, void *user_data) { > > + const char *prefix = user_data; > > + > > + if (tester_use_debug()) > > + tester_debug("%s %s", prefix, str); } > > + > > +static void test_teardown(const void *user_data) { > > + struct test_data *data = (void *)user_data; > > + > > + bt_gatt_client_unref(data->client); > > + util_iov_free(data->iov, data->iovcnt); > > + gatt_db_unref(data->db); > > + > > + tester_teardown_complete(); > > +} > > + > > +static void test_complete_cb(const void *user_data) { > > + tester_test_passed(); > > +} > > + > > +static void client_ready_cb(bool success, uint8_t att_ecode, void > > +*user_data) { > > + > > + if (!success) > > + tester_setup_failed(); > > + else > > + tester_setup_complete(); } > > + > > +static void micp_write_cb(bool success, uint8_t att_ecode, void > > +*user_data) { > > + if (success) > > + printf("MICP Write successful\n"); > > + else > > + printf("\nWrite failed: 0x%02x\n", att_ecode); } > > + > > +static void micp_write_value(struct bt_micp *micp, void *user_data) { > > + struct bt_mics *mics = micp_get_mics(micp); > > + uint16_t value_handle; > > + int ret; > > + const uint16_t value = 0x0001; > > + > > + gatt_db_attribute_get_char_data(mics->ms, NULL, &value_handle, > > + NULL, NULL, > > + NULL); > > + > > + printf("%s handle: %x\n", __func__, value_handle); > > + ret = bt_gatt_client_write_value(micp->client, value_handle, > > + (void *)&value, sizeof(value), micp_write_cb, NULL, > > + NULL); > > + > > + if (!ret) > > + printf("bt_gatt_client_write_value() : Write FAILED"); > > +} > > + > > +static void micp_ready(struct bt_micp *micp, void *user_data) { > > + micp_write_value(micp, user_data); } > > + > > +static void test_client(const void *user_data) { > > + struct test_data *data = (void *)user_data; > > + struct io *io; > > + > > + io = tester_setup_io(data->iov, data->iovcnt); > > + g_assert(io); > > + > > + tester_io_set_complete_func(test_complete_cb); > > + > > + data->db = gatt_db_new(); > > + g_assert(data->db); > > + > > + data->micp = bt_micp_new(data->db, bt_gatt_client_get_db(data- > >client)); > > + g_assert(data->micp); > > + > > + bt_micp_set_debug(data->micp, print_debug, "bt_mip: ", NULL); > > + > > + bt_micp_ready_register(data->micp, micp_ready, data, NULL); > > + > > + bt_micp_attach(data->micp, data->client); } > > + > > + /* ATT: Exchange MTU Response (0x03) len 2 > > + * Server RX MTU: 64 > > + */ > > + /* ATT: Exchange MTU Request (0x02) len 2 > > + * Client RX MTU: 64 > > + */ > > +#define ATT_EXCHANGE_MTU IOV_DATA(0x02, 0x40, 0x00), \ > > + IOV_DATA(0x03, 0x40, 0x00) > > + > > +/* > > + * ATT: Read By Type Request (0x08) len 6 > > + * Handle range: 0x0001-0xffff > > + * Attribute type: Server Supported Features (0x2b3a) > > + */ > > +#define MICP_READ_SR_FEATURE IOV_DATA(0x08, 0x01, 0x00, 0Xff, 0xff, > \ > > + 0x3a, 0x2b), \ > > + IOV_DATA(0x01, 0x08, 0x01, 0x00, 0x0a) > > + > > + /* > > + * ATT: Read By Group Type Request (0x10) len 6 > > + * Handle range: 0x0001-0xffff > > + * Attribute group type: Primary Service (0x2800) > > + */ > > + > > +/* > > + * ATT: Read By Group Type Response (0x11) len 7 > > + * Attribute data length: 6 > > + * Attribute group list: 1 entry > > + * Handle range: 0x00a0-0x00a4 > > + * UUID: Microphone Control (0x184d) > > + */ > > +#define MICP_READ_GROUP_TYPE \ > > + IOV_DATA(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ > > + IOV_DATA(0x11, 0x06, \ > > + 0x01, 0x00, 0x04, 0x00, 0x4d, 0x18), \ > > + IOV_DATA(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \ > > + IOV_DATA(0x01, 0x10, 0x06, 0x00, 0x0a) > > + > > + /* ATT: Read By Group Type Request (0x10) len 6 > > + * Handle range: 0x0001-0xffff > > + * Attribute group type: Secondary Service (0x2801) > > + */ > > + /* ATT: Error Response (0x01) len 4 > > + * Read By Group Type Request (0x10) > > + * Handle: 0x0001 > > + * Error: Attribute Not Found (0x0a)08 01 00 05 00 02 28 > > + */ > > +#define MICP_READ_REQ_SECOND_SERVICE \ > > + IOV_DATA(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \ > > + IOV_DATA(0x01, 0x10, 0x01, 0x00, 0x0a) > > + > > +#define MICP_READ_REQ_INCLUDE_SERVICE \ > > + IOV_DATA(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28), \ > > + IOV_DATA(0x01, 0x08, 0x01, 0x00, 0x0a) > > + > > + /* ATT: Read By Type Request (0x08) len 6 > > + * Handle range: 0x0001-0x0004 > > + * Attribute type: Characteristic (0x2803) > > + */ > > + > > +/* ATT: Find Information Request (0x04) len 4 > > + * Handle range: 0x0004-0x0004 > > + */ > > +#define MICP_FIND_INFO_REQ \ > > + IOV_DATA(0x04, 0x04, 0x00, 0x04, 0x00), \ > > + IOV_DATA(0x05, 0x01, 0x04, 0x00, 0x02, 0x29) > > + > > +/* > > + * ATT: Read By Type Request (0x08) len 6 > > + * Handle range: 0x0001-0x0004 > > + * Attribute type: Characteristic (0x2803) > > + */ > > +#define MICP_READ_REQ_CHAR \ > > + IOV_DATA(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),\ > > + IOV_DATA(0x09, 0x07, \ > > + 0x02, 0x00, 0x1a, 0x03, 0x00, 0xc3, 0x2b), \ > > + IOV_DATA(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28), \ > > + IOV_DATA(0x01, 0x08, 0x04, 0x00, 0x0a) > > + > > +#define MICS_MUTE_READ \ > > + IOV_DATA(0x0a, 0x03, 0x00), \ > > + IOV_DATA(0x0b, 0x01) > > + > > +#define MICS_EN_MUTE_DISCPTR \ > > + IOV_DATA(0x12, 0x04, 0x00, 0x01, 0x00), \ > > + IOV_DATA(0x13) > > + > > +#define MICS_MUTE_WRITE \ > > + IOV_DATA(0x12, 0x03, 0x00, 0x01),\ > > + IOV_DATA(0x13) > > + > > +#define MICP_CL_CGGIT_SER_BV_01_C \ > > + MICS_MUTE_READ, \ > > + MICS_EN_MUTE_DISCPTR, \ > > + IOV_DATA(0x12, 0x03, 0x00, 0x01, 0x00), \ > > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x013) > > + > > +#define MICP_CL_CGGIT_CHA_BV_01_C \ > > + MICS_MUTE_READ, \ > > + MICS_EN_MUTE_DISCPTR, \ > > + IOV_DATA(0x12, 0x03, 0x00, 0x06, 0x00), \ > > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x013), \ > > + MICS_MUTE_READ > > + > > +#define MICP_CL_SPE_BI_01_C \ > > + MICS_MUTE_READ, \ > > + MICS_EN_MUTE_DISCPTR, \ > > + IOV_DATA(0x12, 0x03, 0x00, 0x01, 0x00), \ > > + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x80) > > + > > +/* GATT Discover All procedure */ > > +static const struct iovec setup_data[] = { > > + ATT_EXCHANGE_MTU, > > + MICP_READ_SR_FEATURE, > > + MICP_READ_GROUP_TYPE, > > + MICP_READ_REQ_SECOND_SERVICE, > > + MICP_READ_REQ_INCLUDE_SERVICE, > > + MICP_READ_REQ_CHAR, > > + MICP_FIND_INFO_REQ }; > > + > > +static void test_setup(const void *user_data) { > > + struct test_data *data = (void *)user_data; > > + struct bt_att *att; > > + struct gatt_db *db; > > + struct io *io; > > + > > + io = tester_setup_io(setup_data, ARRAY_SIZE(setup_data)); > > + g_assert(io); > > + > > + att = bt_att_new(io_get_fd(io), false); > > + g_assert(att); > > + > > + bt_att_set_debug(att, BT_ATT_DEBUG, print_debug, "bt_att:", > > + NULL); > > + > > + db = gatt_db_new(); > > + g_assert(db); > > + > > + > > + data->client = bt_gatt_client_new(db, att, MICP_GATT_CLIENT_MTU, > 0); > > + g_assert(data->client); > > + > > + bt_gatt_client_set_debug(data->client, print_debug, "bt_gatt_client:", > > + NULL); > > + > > + bt_gatt_client_ready_register(data->client, client_ready_cb, data, > > + NULL); > > + > > + bt_att_unref(att); > > + gatt_db_unref(db); > > +} > > + > > + > > +int main(int argc, char *argv[]) > > +{ > > + > > + tester_init(&argc, &argv); > > + > > + define_test("MICP/CL/CGGIT/SER/BV-01-C", test_client, NULL, > > + MICS_MUTE_READ); > > + define_test("MICP/CL/CGGIT/CHA/BV-01-C", test_client, NULL, > > + MICP_CL_CGGIT_SER_BV_01_C); > > + define_test("MICP/CL/SPE/BI-01-C", test_client, NULL, > > + MICP_CL_SPE_BI_01_C); > > + > > + return tester_run(); > > +} > > -- > > 2.34.1 > > > > > -- > Luiz Augusto von Dentz
diff --git a/Makefile.am b/Makefile.am index 6f40f2a74..cde55bebf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -573,6 +573,19 @@ unit_test_gattrib_LDADD = src/libshared-glib.la \ lib/libbluetooth-internal.la \ $(GLIB_LIBS) $(DBUS_LIBS) -ldl -lrt +unit_tests += unit/test-micp + +unit_test_micp_SOURCES = unit/test-micp.c + +unit_test_micp_LDADD = src/libshared-glib.la \ + lib/libbluetooth-internal.la $(GLIB_LIBS) + +unit_tests += unit/test-mics + +unit_test_mics_SOURCES = unit/test-mics.c +unit_test_mics_LDADD = src/libshared-glib.la \ + lib/libbluetooth-internal.la $(GLIB_LIBS) + unit_tests += unit/test-bap unit_test_bap_SOURCES = unit/test-bap.c diff --git a/unit/test-micp.c b/unit/test-micp.c new file mode 100644 index 000000000..3db32a4f7 --- /dev/null +++ b/unit/test-micp.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2023 NXP Semiconductors. All rights reserved. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#define _GNU_SOURCE +#include <unistd.h> +#include <string.h> +#include <sys/socket.h> +#include <fcntl.h> + + +#include <glib.h> + +#include "lib/bluetooth.h" +#include "lib/uuid.h" +#include "src/shared/util.h" +#include "src/shared/tester.h" +#include "src/shared/queue.h" +#include "src/shared/att.h" +#include "src/shared/gatt-db.h" +#include "src/shared/gatt-server.h" +#include "src/shared/micp.h" + +struct test_data { + struct gatt_db *db; + struct bt_micp *micp; + struct bt_gatt_server *server; + struct bt_gatt_client *client; + struct queue *ccc_states; + size_t iovcnt; + struct iovec *iov; + struct test_config *cfg; +}; + +struct ccc_state { + uint16_t handle; + uint16_t value; +}; + +struct notify { + uint16_t handle, ccc_handle; + uint8_t *value; + uint16_t len; + bt_gatt_server_conf_func_t conf; + void *user_data; +}; + +#define iov_data(args...) ((const struct iovec[]) { args }) + +#define define_test(name, function, _cfg, args...) \ + do { \ + const struct iovec iov[] = { args }; \ + static struct test_data data; \ + data.cfg = _cfg; \ + data.iovcnt = ARRAY_SIZE(iov_data(args)); \ + data.iov = util_iov_dup(iov, ARRAY_SIZE(iov_data(args))); \ + tester_add(name, &data, NULL, function, \ + test_teardown); \ + } while (0) + +static void print_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + + if (tester_use_debug()) + tester_debug("%s%s", prefix, str); +} + +static void test_teardown(const void *user_data) +{ + struct test_data *data = (void *)user_data; + + bt_micp_unref(data->micp); + bt_gatt_server_unref(data->server); + util_iov_free(data->iov, data->iovcnt); + gatt_db_unref(data->db); + + queue_destroy(data->ccc_states, free); + + tester_teardown_complete(); +} + +static void test_complete_cb(const void *user_data) +{ + tester_test_passed(); +} + +static bool ccc_state_match(const void *a, const void *b) +{ + const struct ccc_state *ccc = a; + uint16_t handle = PTR_TO_UINT(b); + + return ccc->handle == handle; +} + +static struct ccc_state *find_ccc_state(struct test_data *data, + uint16_t handle) +{ + return queue_find(data->ccc_states, ccc_state_match, + UINT_TO_PTR(handle)); +} + +static struct ccc_state *get_ccc_state(struct test_data *data, uint16_t handle) +{ + struct ccc_state *ccc; + + ccc = find_ccc_state(data, handle); + if (ccc) + return ccc; + + ccc = new0(struct ccc_state, 1); + ccc->handle = handle; + queue_push_tail(data->ccc_states, ccc); + + return ccc; +} + +static void gatt_notify_cb(struct gatt_db_attribute *attrib, + struct gatt_db_attribute *ccc, + const uint8_t *value, size_t len, + struct bt_att *att, void *user_data) +{ + struct test_data *data = user_data; + struct notify notify; + + memset(¬ify, 0, sizeof(notify)); + + notify.handle = gatt_db_attribute_get_handle(attrib); + notify.ccc_handle = gatt_db_attribute_get_handle(ccc); + notify.value = (void *) value; + notify.len = len; + + printf("%s: notify.value:%d notify->len:%d\n", __func__, + (int)*(notify.value), notify.len); + if (!bt_gatt_server_send_notification(data->server, + notify.handle, notify.value, + notify.len, false)) + printf("%s: Failed to send notification\n", __func__); +} + +static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct test_data *data = user_data; + struct ccc_state *ccc; + uint16_t handle; + uint8_t ecode = 0; + const uint8_t *value = NULL; + size_t len = 0; + + handle = gatt_db_attribute_get_handle(attrib); + + ccc = get_ccc_state(data, handle); + if (!ccc) { + ecode = BT_ATT_ERROR_UNLIKELY; + goto done; + } + + len = sizeof(ccc->value); + value = (void *) &ccc->value; + +done: + gatt_db_attribute_read_result(attrib, id, ecode, value, len); +} + +static void test_server(const void *user_data) +{ + struct test_data *data = (void *)user_data; + struct bt_att *att; + struct io *io; + + io = tester_setup_io(data->iov, data->iovcnt); + g_assert(io); + + tester_io_set_complete_func(test_complete_cb); + + att = bt_att_new(io_get_fd(io), false); + g_assert(att); + + bt_att_set_debug(att, BT_ATT_DEBUG, print_debug, "bt_att:", NULL); + + data->db = gatt_db_new(); + g_assert(data->db); + + gatt_db_ccc_register(data->db, gatt_ccc_read_cb, NULL, + gatt_notify_cb, data); + + data->micp = bt_micp_new(data->db, NULL); + g_assert(data->micp); + + data->server = bt_gatt_server_new(data->db, att, 64, 0); + g_assert(data->server); + + bt_gatt_server_set_debug(data->server, print_debug, "bt_gatt_server:", + NULL); + + data->ccc_states = queue_new(); + + tester_io_send(); + + bt_att_unref(att); +} + +#define EXCHANGE_MTU IOV_DATA(0x02, 0x40, 0x00), \ + IOV_DATA(0x03, 0x40, 0x00) + +#define MICS_MUTE_WRITE_VAL_00 \ + IOV_DATA(0x12, 0x03, 0x00, 0x00), \ + IOV_DATA(0x13) + +#define MICS_MUTE_WRITE_VAL_01 \ + IOV_DATA(0x12, 0x03, 0x00, 0x01), \ + IOV_DATA(0x13) + +#define MICS_MUTE_READ \ + IOV_DATA(0x0a, 0x03, 0x00), \ + IOV_DATA(0x0b, 0x01) + +#define DISCOVER_PRIM_SERV_NOTIF \ + IOV_DATA(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ + IOV_DATA(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x4d, 0x18), \ + IOV_DATA(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \ + IOV_DATA(0x01, 0x10, 0x05, 0x00, 0x0a) + +/* ATT: Read By Type Request (0x08) len 6 + * Handle range: 0x0001-0x0009 + * Attribute type: Characteristic (0x2803) + * ATT: Read By Type Response (0x09) len 22 + * Attribute data length: 7 + * Handle: 0x0002 + * Value: 1a0300c82b + * Properties: 0x1a + * Value Handle: 0x0003 + * Value UUID: Mute (0x2bc3) + */ +#define DISC_MICS_CHAR_1 \ + IOV_DATA(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28), \ + IOV_DATA(0x09, 0x07, \ + 0x02, 0x00, 0x1a, 0x03, 0x00, 0xc3, 0x2b), \ + IOV_DATA(0x08, 0x05, 0x00, 0x05, 0x00, 0x03, 0x28), \ + IOV_DATA(0x01, 0x08, 0x05, 0x00, 0x0a) + + +#define MICS_FIND_BY_TYPE_VALUE \ + IOV_DATA(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x4d, 0x18), \ + IOV_DATA(0x07, 0x01, 0x00, 0x04, 0x00), \ + IOV_DATA(0x06, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28, 0x4d, 0x18), \ + IOV_DATA(0x01, 0x06, 0x05, 0x00, 0x0a) + +#define DISC_MICS_CHAR_AFTER_TYPE \ + IOV_DATA(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28), \ + IOV_DATA(0x09, 0x07, \ + 0x02, 0x00, 0x1a, 0x03, 0x00, 0xc3, 0x2b), \ + IOV_DATA(0x08, 0x03, 0x00, 0x05, 0x00, 0x03, 0x28), \ + IOV_DATA(0x01, 0x08, 0x03, 0x00, 0x0a) + +#define MICS_WRITE_CCD \ + IOV_DATA(0x12, 0x04, 0x00, 0x00, 0x00), \ + IOV_DATA(0x13), \ + IOV_DATA(0x12, 0x04, 0x00, 0x01, 0x00), \ + IOV_DATA(0x13) + +#define MICS_FIND_INFO \ + IOV_DATA(0x04, 0x04, 0x00, 0x05, 0x00), \ + IOV_DATA(0x05, 0x01, 0x04, 0x00, 0x02, 0x29), \ + IOV_DATA(0x04, 0x05, 0x00, 0x05, 0x00), \ + IOV_DATA(0x01, 0x04, 0x05, 0x00, 0x0a) + +#define MICS_SR_SPN_BV_01_C \ + EXCHANGE_MTU, \ + DISCOVER_PRIM_SERV_NOTIF, \ + DISC_MICS_CHAR_1, \ + MICS_FIND_BY_TYPE_VALUE, \ + DISC_MICS_CHAR_AFTER_TYPE, \ + MICS_FIND_INFO, \ + MICS_WRITE_CCD, \ + IOV_DATA(0x0a, 0x03, 0x00), \ + IOV_DATA(0x0b, 0x01), \ + MICS_MUTE_WRITE_VAL_00, \ + IOV_DATA(0x1b, 0x03, 0x00, 0x00), \ + MICS_MUTE_WRITE_VAL_01, \ + IOV_DATA(0x1b, 0x03, 0x00, 0x01), \ + IOV_DATA(0x0a, 0x03, 0x00), \ + IOV_DATA(0x0b, 0x01) + +#define MICS_SR_SGGIT_SER_BV_01_C \ + EXCHANGE_MTU, \ + DISCOVER_PRIM_SERV_NOTIF, \ + MICS_FIND_BY_TYPE_VALUE + +#define MICS_SR_SGGIT_CHA_BV_01_C \ + EXCHANGE_MTU, \ + DISCOVER_PRIM_SERV_NOTIF, \ + MICS_FIND_BY_TYPE_VALUE, \ + DISC_MICS_CHAR_AFTER_TYPE + +#define MICS_WRITE_MUTE_CHAR_INVALID \ + IOV_DATA(0x12, 0x03, 0x00, 0x02), \ + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x13), \ + IOV_DATA(0x12, 0x03, 0x00, 0x05), \ + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x13) + +#define MICS_SR_SPE_BI_1_C \ + EXCHANGE_MTU, \ + DISCOVER_PRIM_SERV_NOTIF, \ + MICS_FIND_BY_TYPE_VALUE, \ + MICS_WRITE_MUTE_CHAR_INVALID + +#define MICS_MUTE_READ_INVALID \ + IOV_DATA(0x0a, 0x03, 0x00), \ + IOV_DATA(0x0b, 0x02) + +#define MICS_MUTE_WRITE_1 \ + IOV_DATA(0x12, 0x03, 0x00, 0x01), \ + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x80) + +#define MICS_MUTE_WRITE_0 \ + IOV_DATA(0x12, 0x03, 0x00, 0x00), \ + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x80) + +#define MICS_SR_SPE_BI_02_C \ + EXCHANGE_MTU, \ + DISCOVER_PRIM_SERV_NOTIF, \ + MICS_FIND_BY_TYPE_VALUE, \ + MICS_MUTE_READ_INVALID, \ + MICS_MUTE_WRITE_0, \ + MICS_MUTE_WRITE_1 + +int main(int argc, char *argv[]) +{ + + tester_init(&argc, &argv); + + define_test("MICS/SR/SGGIT/SER/BV-01-C", test_server, NULL, + MICS_SR_SGGIT_SER_BV_01_C); + define_test("MICS/SR/SGGIT/CHA/BV-01-C", test_server, NULL, + MICS_SR_SGGIT_CHA_BV_01_C); + define_test("MICS/SR/SPE/BI-01-C", test_server, NULL, + MICS_SR_SPE_BI_1_C); + define_test("MICS/SR/SPE/BI-02-C", test_server, NULL, + MICS_SR_SPE_BI_02_C); + define_test("MICS/SR/SPN/BV-01-C", test_server, NULL, + MICS_SR_SPN_BV_01_C); + + return tester_run(); +} diff --git a/unit/test-mics.c b/unit/test-mics.c new file mode 100644 index 000000000..7a7d70bf3 --- /dev/null +++ b/unit/test-mics.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2023 NXP Semiconductors. All rights reserved. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#define _GNU_SOURCE +#include <unistd.h> +#include <string.h> +#include <sys/socket.h> +#include <fcntl.h> + + +#include <glib.h> + +#include "lib/bluetooth.h" +#include "lib/uuid.h" +#include "btio/btio.h" +#include "src/shared/util.h" +#include "src/shared/tester.h" +#include "src/shared/queue.h" +#include "src/shared/att.h" +#include "src/shared/gatt-db.h" +#include "src/shared/gatt-helpers.h" +#include "src/shared/micp.h" + +struct test_data { + struct gatt_db *db; + struct bt_mics *mics; + struct bt_micp *micp; + struct bt_gatt_client *client; + size_t iovcnt; + struct iovec *iov; + struct test_config *cfg; +}; + +struct db_attribute_micp_test_data { + struct gatt_db_attribute *match; + bool found; +}; + +#define MICP_GATT_CLIENT_MTU 64 +#define iov_data(args...) ((const struct iovec[]) { args }) + +#define define_test(name, function, _cfg, args...) \ + do { \ + const struct iovec iov[] = { args }; \ + static struct test_data data; \ + data.cfg = _cfg; \ + data.iovcnt = ARRAY_SIZE(iov_data(args)); \ + data.iov = util_iov_dup(iov, ARRAY_SIZE(iov_data(args))); \ + tester_add(name, &data, test_setup, function, \ + test_teardown); \ + } while (0) + +static void print_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + + if (tester_use_debug()) + tester_debug("%s %s", prefix, str); +} + +static void test_teardown(const void *user_data) +{ + struct test_data *data = (void *)user_data; + + bt_gatt_client_unref(data->client); + util_iov_free(data->iov, data->iovcnt); + gatt_db_unref(data->db); + + tester_teardown_complete(); +} + +static void test_complete_cb(const void *user_data) +{ + tester_test_passed(); +} + +static void client_ready_cb(bool success, uint8_t att_ecode, void *user_data) +{ + + if (!success) + tester_setup_failed(); + else + tester_setup_complete(); +} + +static void micp_write_cb(bool success, uint8_t att_ecode, void *user_data) +{ + if (success) + printf("MICP Write successful\n"); + else + printf("\nWrite failed: 0x%02x\n", att_ecode); +} + +static void micp_write_value(struct bt_micp *micp, void *user_data) +{ + struct bt_mics *mics = micp_get_mics(micp); + uint16_t value_handle; + int ret; + const uint16_t value = 0x0001; + + gatt_db_attribute_get_char_data(mics->ms, NULL, &value_handle, + NULL, NULL, NULL); + + printf("%s handle: %x\n", __func__, value_handle); + ret = bt_gatt_client_write_value(micp->client, value_handle, + (void *)&value, sizeof(value), micp_write_cb, NULL, NULL); + + if (!ret) + printf("bt_gatt_client_write_value() : Write FAILED"); +} + +static void micp_ready(struct bt_micp *micp, void *user_data) +{ + micp_write_value(micp, user_data); +} + +static void test_client(const void *user_data) +{ + struct test_data *data = (void *)user_data; + struct io *io; + + io = tester_setup_io(data->iov, data->iovcnt); + g_assert(io); + + tester_io_set_complete_func(test_complete_cb); + + data->db = gatt_db_new(); + g_assert(data->db); + + data->micp = bt_micp_new(data->db, bt_gatt_client_get_db(data->client)); + g_assert(data->micp); + + bt_micp_set_debug(data->micp, print_debug, "bt_mip: ", NULL); + + bt_micp_ready_register(data->micp, micp_ready, data, NULL); + + bt_micp_attach(data->micp, data->client); +} + + /* ATT: Exchange MTU Response (0x03) len 2 + * Server RX MTU: 64 + */ + /* ATT: Exchange MTU Request (0x02) len 2 + * Client RX MTU: 64 + */ +#define ATT_EXCHANGE_MTU IOV_DATA(0x02, 0x40, 0x00), \ + IOV_DATA(0x03, 0x40, 0x00) + +/* + * ATT: Read By Type Request (0x08) len 6 + * Handle range: 0x0001-0xffff + * Attribute type: Server Supported Features (0x2b3a) + */ +#define MICP_READ_SR_FEATURE IOV_DATA(0x08, 0x01, 0x00, 0Xff, 0xff, \ + 0x3a, 0x2b), \ + IOV_DATA(0x01, 0x08, 0x01, 0x00, 0x0a) + + /* + * ATT: Read By Group Type Request (0x10) len 6 + * Handle range: 0x0001-0xffff + * Attribute group type: Primary Service (0x2800) + */ + +/* + * ATT: Read By Group Type Response (0x11) len 7 + * Attribute data length: 6 + * Attribute group list: 1 entry + * Handle range: 0x00a0-0x00a4 + * UUID: Microphone Control (0x184d) + */ +#define MICP_READ_GROUP_TYPE \ + IOV_DATA(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ + IOV_DATA(0x11, 0x06, \ + 0x01, 0x00, 0x04, 0x00, 0x4d, 0x18), \ + IOV_DATA(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \ + IOV_DATA(0x01, 0x10, 0x06, 0x00, 0x0a) + + /* ATT: Read By Group Type Request (0x10) len 6 + * Handle range: 0x0001-0xffff + * Attribute group type: Secondary Service (0x2801) + */ + /* ATT: Error Response (0x01) len 4 + * Read By Group Type Request (0x10) + * Handle: 0x0001 + * Error: Attribute Not Found (0x0a)08 01 00 05 00 02 28 + */ +#define MICP_READ_REQ_SECOND_SERVICE \ + IOV_DATA(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \ + IOV_DATA(0x01, 0x10, 0x01, 0x00, 0x0a) + +#define MICP_READ_REQ_INCLUDE_SERVICE \ + IOV_DATA(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28), \ + IOV_DATA(0x01, 0x08, 0x01, 0x00, 0x0a) + + /* ATT: Read By Type Request (0x08) len 6 + * Handle range: 0x0001-0x0004 + * Attribute type: Characteristic (0x2803) + */ + +/* ATT: Find Information Request (0x04) len 4 + * Handle range: 0x0004-0x0004 + */ +#define MICP_FIND_INFO_REQ \ + IOV_DATA(0x04, 0x04, 0x00, 0x04, 0x00), \ + IOV_DATA(0x05, 0x01, 0x04, 0x00, 0x02, 0x29) + +/* + * ATT: Read By Type Request (0x08) len 6 + * Handle range: 0x0001-0x0004 + * Attribute type: Characteristic (0x2803) + */ +#define MICP_READ_REQ_CHAR \ + IOV_DATA(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),\ + IOV_DATA(0x09, 0x07, \ + 0x02, 0x00, 0x1a, 0x03, 0x00, 0xc3, 0x2b), \ + IOV_DATA(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28), \ + IOV_DATA(0x01, 0x08, 0x04, 0x00, 0x0a) + +#define MICS_MUTE_READ \ + IOV_DATA(0x0a, 0x03, 0x00), \ + IOV_DATA(0x0b, 0x01) + +#define MICS_EN_MUTE_DISCPTR \ + IOV_DATA(0x12, 0x04, 0x00, 0x01, 0x00), \ + IOV_DATA(0x13) + +#define MICS_MUTE_WRITE \ + IOV_DATA(0x12, 0x03, 0x00, 0x01),\ + IOV_DATA(0x13) + +#define MICP_CL_CGGIT_SER_BV_01_C \ + MICS_MUTE_READ, \ + MICS_EN_MUTE_DISCPTR, \ + IOV_DATA(0x12, 0x03, 0x00, 0x01, 0x00), \ + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x013) + +#define MICP_CL_CGGIT_CHA_BV_01_C \ + MICS_MUTE_READ, \ + MICS_EN_MUTE_DISCPTR, \ + IOV_DATA(0x12, 0x03, 0x00, 0x06, 0x00), \ + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x013), \ + MICS_MUTE_READ + +#define MICP_CL_SPE_BI_01_C \ + MICS_MUTE_READ, \ + MICS_EN_MUTE_DISCPTR, \ + IOV_DATA(0x12, 0x03, 0x00, 0x01, 0x00), \ + IOV_DATA(0x01, 0x12, 0x03, 0x00, 0x80) + +/* GATT Discover All procedure */ +static const struct iovec setup_data[] = { + ATT_EXCHANGE_MTU, + MICP_READ_SR_FEATURE, + MICP_READ_GROUP_TYPE, + MICP_READ_REQ_SECOND_SERVICE, + MICP_READ_REQ_INCLUDE_SERVICE, + MICP_READ_REQ_CHAR, + MICP_FIND_INFO_REQ +}; + +static void test_setup(const void *user_data) +{ + struct test_data *data = (void *)user_data; + struct bt_att *att; + struct gatt_db *db; + struct io *io; + + io = tester_setup_io(setup_data, ARRAY_SIZE(setup_data)); + g_assert(io); + + att = bt_att_new(io_get_fd(io), false); + g_assert(att); + + bt_att_set_debug(att, BT_ATT_DEBUG, print_debug, "bt_att:", NULL); + + db = gatt_db_new(); + g_assert(db); + + + data->client = bt_gatt_client_new(db, att, MICP_GATT_CLIENT_MTU, 0); + g_assert(data->client); + + bt_gatt_client_set_debug(data->client, print_debug, "bt_gatt_client:", + NULL); + + bt_gatt_client_ready_register(data->client, client_ready_cb, data, + NULL); + + bt_att_unref(att); + gatt_db_unref(db); +} + + +int main(int argc, char *argv[]) +{ + + tester_init(&argc, &argv); + + define_test("MICP/CL/CGGIT/SER/BV-01-C", test_client, NULL, + MICS_MUTE_READ); + define_test("MICP/CL/CGGIT/CHA/BV-01-C", test_client, NULL, + MICP_CL_CGGIT_SER_BV_01_C); + define_test("MICP/CL/SPE/BI-01-C", test_client, NULL, + MICP_CL_SPE_BI_01_C); + + return tester_run(); +}