From patchwork Thu Aug 3 07:20:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mahesh Talewad X-Patchwork-Id: 709709 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EBDB3C001DF for ; Thu, 3 Aug 2023 07:21:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233786AbjHCHU7 (ORCPT ); Thu, 3 Aug 2023 03:20:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38766 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233772AbjHCHU5 (ORCPT ); Thu, 3 Aug 2023 03:20:57 -0400 Received: from EUR04-DB3-obe.outbound.protection.outlook.com (mail-db3eur04on2082.outbound.protection.outlook.com [40.107.6.82]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4F8252D65 for ; Thu, 3 Aug 2023 00:20:54 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=NXXaayays8UauDzQu24OQZaTVsQm3VB/fT1vaTJiiDcftwtfNNhAvH9zhTtwRgr0gPA8p1LEanOOpctM/1SXEa91+2Fa4M1W8Q9H4mYOhBm5IGvT6RQMGdNkMDLyAyZKCZc0WAqOGFEC1PbO0CDaCLIBYBXvELa8Mso9+6tOkdHs5yvfUWyJc42wSCMLSYkoksJLS+wxRqrhh3SLAIm5eGTL99PfemaUyBZrygUKzj6xWyX0f26faMFiHwgfdYj1xEVHbd+gbpAV1D/2zflP275tho/mOHgeR2qMaETPQu7QVkB7614riRZPppfgCHy2AktLW83ov2mfK/z4Y+aUPQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=5cC+ZWpJOH2ZNglVKIi6ZvwW5b8+/4otqVlALsLy2hk=; b=MPJSNbvyKd5n1dxmyHsiATukThBohhWmnbWlfqPd3l9Emjp6DB1sAAzTxWJYPu8fUa7nlhkY+YS1xsVqyuryZ5WCSpJRqiYyddiIVVtmP4Y2T3wUG4Q1i5vvW4MFfK8F3ZodHS3mlU99u3y+pgqWHiTiFcTRdeYaJYpwl77fXi0+ORVmoddJs+5EvKeGgGDjT6Dni2lJmirQoKfabE9v+SHPGDZMCQU72Drs4+2n+++UoUXgYutm0nc++b2zU/9mEqAIzWoI8fqnsOw2kXXk26BH6+hs3ZQl6MA7fdCUm/h88ywbSuYv2tlXFj/sDi5y45vZpeAO3wwOqpniny8sNQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=5cC+ZWpJOH2ZNglVKIi6ZvwW5b8+/4otqVlALsLy2hk=; b=LJCDQfAYFtzchaTMFxVkPRhWjCIiLlf+7jFpYYTa6cmOVXH3HoUl3bMWznig0QIbijcEBJBGLuVp/jrvspNVClfFi3txuQpBfSTuqpl0kD67mu9xFQ0b5d7skoq3EAaJF1XMJssIsMylIo/3gAVvfG/A22bQbqCpq0yVntvZ1uc= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from VI1PR04MB6991.eurprd04.prod.outlook.com (2603:10a6:803:12d::13) by DU0PR04MB9418.eurprd04.prod.outlook.com (2603:10a6:10:359::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6631.47; Thu, 3 Aug 2023 07:20:50 +0000 Received: from VI1PR04MB6991.eurprd04.prod.outlook.com ([fe80::4299:5db1:461:bc17]) by VI1PR04MB6991.eurprd04.prod.outlook.com ([fe80::4299:5db1:461:bc17%7]) with mapi id 15.20.6631.046; Thu, 3 Aug 2023 07:20:50 +0000 From: Mahesh Talewad To: linux-bluetooth@vger.kernel.org Cc: luiz.dentz@gmail.com, devyani.godbole@nxp.com, nitin.jadhav@nxp.com, mahesh.talewad@nxp.com Subject: [PATCH BlueZ v1 1/3] shared/micp: Add initial code for handling micp and mics Date: Thu, 3 Aug 2023 10:20:09 +0300 Message-Id: <20230803072011.191449-2-mahesh.talewad@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230803072011.191449-1-mahesh.talewad@nxp.com> References: <20230803072011.191449-1-mahesh.talewad@nxp.com> X-ClientProxiedBy: SG2PR01CA0125.apcprd01.prod.exchangelabs.com (2603:1096:4:40::29) To VI1PR04MB6991.eurprd04.prod.outlook.com (2603:10a6:803:12d::13) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: VI1PR04MB6991:EE_|DU0PR04MB9418:EE_ X-MS-Office365-Filtering-Correlation-Id: 54d0dfaf-4840-4e33-bf7b-08db93f22a20 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: tHXPCmRRBKKFs0oXWfGiCSXuwSplSJTlP22LemDYvLbkwXCQHqu3hK4rATHY+YmefXtCGbelBnT4hFWHHj2tIwRv7g0T2YeS91GYq2Cr1dgmMR85MTCVwEpfkO0Ow/TV1U+zL/ORVFPx+q/sPfAzJt42bICR9JnMYGNIK8VMiwLD9wn9Esn90Q2yHmd1XB4vjm2zAZiNFydwy04AfGufXZWCEDCCig5haquZZWISmgf8tkc8vcFgV3ErI+kZWiswsYWAexRr9h/osmnsUMW3s7WhgL0DteXTSSVtYSX7g5WjOpzafCjsMKInnJ7eZXpiJo94uWTNParbq8ESR1DXF5HuovJPDN7fu5+uXsAbMBt8U0PKZsez7Q3K3ERHdyLHLH51u76NL/lA7Ri4NUTNUnckb0FQb2qcRXLkyNt9nRfqKtAz4YutO3FriyJYkDJ0chsQuToHwqvgRYmVIJfojZMGvWpsRFOFNqNg9Bg/c8G82MA+HjyVqEmQ6jTaz0sWUQJYmYaxzG635e1n/I+8KkBooRC0ZDTb6rOBJHIKShKtxzGvCgAr3TONmU8B/M+K301/KEjRvwKGqmTh5932q57x+p7Ca9jyvDCinDly4LTpwja7pyDf2JjKxDYIOhc5 X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:VI1PR04MB6991.eurprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230028)(4636009)(39860400002)(346002)(376002)(396003)(366004)(136003)(451199021)(36756003)(86362001)(52116002)(478600001)(38100700002)(38350700002)(2616005)(1076003)(186003)(26005)(83380400001)(6506007)(55236004)(41300700001)(8676002)(8936002)(44832011)(6512007)(6666004)(6486002)(30864003)(316002)(66476007)(66556008)(5660300002)(6916009)(4326008)(2906002)(66946007); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: lJ1y99k88zFDnoxLR4w1YXLz1rmfDNKQGSS6V78m3WaDpZ6d5QTweh4bww+sKr84vqvy3oIVGPBeO/CzxGYMJ3JkPU2R2E1gFtu0nQDy5/dGvbeDkicasBvRkvBFMr87lmlzyjcr4lFYxsOASoC25yhj05OdSnz2RAEJePvITzVo5IA1szrGj28Hmrm+9B1/eyPq0XhDYDiYNi2opYziH4kXMY8W9wzMCqXmV07K5b1WkdUR1Kn5ZhGjikfbULphENxB1YutfRv0ngdXiSOX4G0j24+A0GUTwz8E/QnE4gH5nBTacpGHSqcWNOJqN2g+0tJJWmsTIpPVckjKf78ZAwxJleQI1v63EhbHmJR8DV0aOter8eqir6dmCTOspK5aeenzuT3uaEcrsMtotZobLW/24LOrfSMfGWy1yi/KwzjCBLRss3JUkRVmaV6B+jNLE9PRA5XK15T3b0DJcvc2gs5s2f3YuGs0oGzAC7wuL2OuKU6147umpcZwepUGSvXdCuFXSSillh7IJuEn0LxDhpAagUmuY1OnXbHDvE2L9o9UwKtJ/1Ds+WebzXYKNaEDtQGqwrDwkiLn9d5my0VWfJ4TMqNyy6Pp5Qh5Mnf4zcKXDyNJGdDz4qcIi/16lCdcgAC/GdAxGrnYWH5hdeTnkxjV0MyBBR4gPY3vLQVpunbgtOsL0ebK8ve+lmGxQ2wfGF/FZtZk19V6vlbGH6Ib+8YJE6BR9tuoC3zb8QxqxjGtw2L51EGIRAKwN2oRTrg+I1lAxyOg6BU22ce88YedUIGx/DbY/Ztk+aGBRWi1MYnQ+345WUMLu+dyg+ns3LM5XjRZys6FhA+i+IofCS/5v4PhV2SpCSsCXZdO8TpUF0PQb+dEkG3c3KqL2q+FinUUAGXrJP+BkYibDj/bTXITsHVka7cYidSZUE90ikoNzDMNxYtp1LeSWx24HbsMfpLjCyDgKGVCyzGG167Q8l0zSi5q/Lb1Scm9jFP2+VRzZZJsFC4t6MDyvp+f9rQWSV0YoIWJQ/6zgRF3qCR6BGuLYpIHRRiWBv5na2LRvFxMelM17lMsqFM+mAuA89ok7GkTysoSSZn4tU8PLX+Ec7fmyzOgXwFaeAUEu2l8SZ/AxaKUEy1sM+yawEvOQS/WKNNLBKVRuwRFd4GXAmfnsdQ2nYt7v+W91OgqToUHnGkAS++LUkv2l4fZE6LRxpdNozSnlCSJ8YgyTf+uKQ61+lfolbYnYfgBIxEylqeURLK/7gosUVmnhz/JuD+IOhGCrPs8YhcQCuWx0Qdk9fi874r4OdFLbicgnlUFcujQXMEoYQyXRNmol9Gc5Jv5b+5FhzTsmAqrpCaa6R6MjHvXSaySsz90Q4ng9L8sAdyrGHBxYBdBq7RXEuqf5e72y6JJGBbBlUyjcqqn5LmDsOm+wXa3FvaiC+PTs4EdGFs2OGNCwK7KV584nxx4GIC2vAAhB2u76BG0gN//1fDafZYV47b0TsjevRRbF6G4OIoTK+nAPGhLH184ZX+poLmxVLVa5bfgxD1Z3TVnDaFSdY8Wp7lvnXHW4wVXR/2VRwzxQ/55c0pFjSFlIuTnEJNCB5g3+Byu X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 54d0dfaf-4840-4e33-bf7b-08db93f22a20 X-MS-Exchange-CrossTenant-AuthSource: VI1PR04MB6991.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 03 Aug 2023 07:20:50.3905 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: DeOJTvbf8ltJxBo2uPIdBLVZ3kpoIl6U1gTPY7uleM6ZBJyfJt5gB8paWPrNcTyn6nRXs5sSqu3y5HR+3gJYmA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DU0PR04MB9418 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org - This commit is for implementations of MICS and MICP - Specifications referred for implementation: MICS - MICS_v1.0.pdf MICP - MICP_v1.0.pdf --- Makefile.am | 1 + Makefile.plugins | 5 + configure.ac | 4 + lib/uuid.h | 4 + profiles/audio/micp.c | 340 ++++++++++++++++ src/shared/micp.c | 922 ++++++++++++++++++++++++++++++++++++++++++ src/shared/micp.h | 46 +++ 7 files changed, 1322 insertions(+) create mode 100644 profiles/audio/micp.c create mode 100644 src/shared/micp.c create mode 100644 src/shared/micp.h diff --git a/Makefile.am b/Makefile.am index 4b9b7e5cd..6f40f2a74 100644 --- a/Makefile.am +++ b/Makefile.am @@ -233,6 +233,7 @@ shared_sources = src/shared/io.h src/shared/timeout.h \ src/shared/bap.h src/shared/bap.c src/shared/ascs.h \ src/shared/mcs.h src/shared/mcp.h src/shared/mcp.c \ src/shared/vcp.c src/shared/vcp.h \ + src/shared/micp.c src/shared/micp.h \ src/shared/csip.c src/shared/csip.h \ src/shared/bass.h src/shared/bass.c \ src/shared/lc3.h src/shared/tty.h diff --git a/Makefile.plugins b/Makefile.plugins index fc19522e4..5880ed0df 100644 --- a/Makefile.plugins +++ b/Makefile.plugins @@ -137,6 +137,11 @@ builtin_modules += vcp builtin_sources += profiles/audio/vcp.c endif +if MICP +builtin_modules += micp +builtin_sources += profiles/audio/micp.c +endif + if CSIP builtin_modules += csip builtin_sources += profiles/audio/csip.c diff --git a/configure.ac b/configure.ac index bc7edfcd3..9a8856380 100644 --- a/configure.ac +++ b/configure.ac @@ -211,6 +211,10 @@ AC_ARG_ENABLE(vcp, AS_HELP_STRING([--disable-vcp], [disable VCP profile]), [enable_vcp=${enableval}]) AM_CONDITIONAL(VCP, test "${enable_vcp}" != "no") +AC_ARG_ENABLE(micp, AS_HELP_STRING([--disable-micp], + [disable MICP profile]), [enable_micp=${enableval}]) +AM_CONDITIONAL(MICP, test "${enable_micp}" != "no") + 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/lib/uuid.h b/lib/uuid.h index cd3b3655f..6661a537f 100644 --- a/lib/uuid.h +++ b/lib/uuid.h @@ -206,6 +206,10 @@ extern "C" { #define CS_LOCK 0x2B86 #define CS_RANK 0x2B87 +/* Microphone Control Service(MICS) */ +#define MICS_UUID 0x184D +#define MUTE_CHRC_UUID 0x2BC3 + typedef struct { enum { BT_UUID_UNSPEC = 0, diff --git a/profiles/audio/micp.c b/profiles/audio/micp.c new file mode 100644 index 000000000..452027c75 --- /dev/null +++ b/profiles/audio/micp.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2023 NXP Semiconductors. All rights reserved. + * + * + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "gdbus/gdbus.h" + +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "src/dbus-common.h" +#include "src/shared/util.h" +#include "src/shared/att.h" +#include "src/shared/queue.h" +#include "src/shared/gatt-db.h" +#include "src/shared/gatt-server.h" +#include "src/shared/micp.h" + +#include "btio/btio.h" +#include "src/plugin.h" +#include "src/adapter.h" +#include "src/gatt-database.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" +#include "src/log.h" +#include "src/error.h" + +#define MICS_UUID_STR "0000184D-0000-1000-8000-00805f9b34fb" + +struct micp_data { + struct btd_device *device; + struct btd_service *service; + struct bt_micp *micp; + unsigned int ready_id; +}; + +static struct queue *sessions; + +static void micp_debug(const char *str, void *user_data) +{ + DBG_IDX(0xffff, "%s", str); +} + +static int micp_disconnect(struct btd_service *service) +{ + return 0; +} + +static struct micp_data *micp_data_new(struct btd_device *device) +{ + struct micp_data *data; + + data = new0(struct micp_data, 1); + g_assert(data); + data->device = device; + + return data; +} + +static void micp_data_add(struct micp_data *data) +{ + DBG("data %p", data); + + if (queue_find(sessions, NULL, data)) { + error("data %p allready added", data); + return; + } + + bt_micp_set_debug(data->micp, micp_debug, NULL, NULL); + + if (!sessions) + sessions = queue_new(); + + queue_push_tail(sessions, data); + + if (data->service) + btd_service_set_user_data(data->service, data); +} + +static bool match_data(const void *data, const void *match_data) +{ + const struct micp_data *mdata = data; + const struct bt_micp *micp = match_data; + + return mdata->micp == micp; +} + +static void micp_data_free(struct micp_data *data) +{ + if (data->service) { + btd_service_set_user_data(data->service, NULL); + bt_micp_set_user_data(data->micp, NULL); + } + + bt_micp_ready_unregister(data->micp, data->ready_id); + bt_micp_unref(data->micp); + free(data); +} + +static void micp_data_remove(struct micp_data *data) +{ + DBG("data %p", data); + + if (!queue_remove(sessions, data)) + return; + + micp_data_free(data); + + if (queue_isempty(sessions)) { + queue_destroy(sessions, NULL); + sessions = NULL; + } +} + +static void micp_detached(struct bt_micp *micp, void *user_data) +{ + struct micp_data *data; + + DBG("%p", micp); + + data = queue_find(sessions, match_data, micp); + if (!data) { + error("unable to find sessio"); + return; + } + + micp_data_remove(data); +} + +static void micp_ready(struct bt_micp *micp, void *user_data) +{ + DBG("micp %p\n", micp); +} + +static void micp_attached(struct bt_micp *micp, void *user_data) +{ + struct micp_data *data; + struct bt_att *att; + struct btd_device *device; + + DBG("%p", micp); + + data = queue_find(sessions, match_data, micp); + if (data) + return; + + att = bt_micp_get_att(micp); + if (!att) + return; + + device = btd_adapter_find_device_by_fd(bt_att_get_fd(att)); + if (!device) { + error("unable to find device"); + return; + } + + data = micp_data_new(device); + g_assert(data); + data->micp = micp; + + micp_data_add(data); +} + +static int micp_probe(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + struct btd_adapter *adapter = device_get_adapter(device); + struct btd_gatt_database *database = btd_adapter_get_database(adapter); + struct micp_data *data = btd_service_get_user_data(service); + char addr[18]; + + ba2str(device_get_address(device), addr); + DBG("%s", addr); + + /*Ignore, if we probed for this device allready */ + if (data) { + error("Profile probed twice for this device"); + return -EINVAL; + } + + data = micp_data_new(device); + data->service = service; + + data->micp = bt_micp_new(btd_gatt_database_get_db(database), + btd_device_get_gatt_db(device)); + + if (!data->micp) { + error("unable to create MICP instance"); + free(data); + return -EINVAL; + } + + micp_data_add(data); + + data->ready_id = bt_micp_ready_register(data->micp, micp_ready, service, + NULL); + + bt_micp_set_user_data(data->micp, service); + + return 0; +} + +static void micp_remove(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + struct micp_data *data; + char addr[18]; + + ba2str(device_get_address(device), addr); + DBG("%s", addr); + + data = btd_service_get_user_data(service); + if (!data) { + error("MICP Service not handled by profile"); + return; + } + + micp_data_remove(data); +} + +static int micp_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 micp_data *data = btd_service_get_user_data(service); + char addr[18]; + + ba2str(device_get_address(device), addr); + DBG("%s", addr); + + if (!data) { + error("MICP Service not handled by profile"); + return -EINVAL; + } + + if (!bt_micp_attach(data->micp, client)) { + error("MICP unable to attach"); + return -EINVAL; + } + + btd_service_connecting_complete(service, 0); + + return 0; +} + +static int micp_connect(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + char addr[18]; + + ba2str(device_get_address(device), addr); + DBG("%s", addr); + + return 0; +} + +static int micp_server_probe(struct btd_profile *p, + struct btd_adapter *adapter) +{ + struct btd_gatt_database *database = btd_adapter_get_database(adapter); + + DBG("MICP path %s", adapter_get_path(adapter)); + + bt_micp_add_db(btd_gatt_database_get_db(database)); + + return 0; +} + +static void micp_server_remove(struct btd_profile *p, + struct btd_adapter *adapter) +{ + DBG("MICP remove adapter"); +} + +static struct btd_profile micp_profile = { + .name = "micp", + .priority = BTD_PROFILE_PRIORITY_MEDIUM, + .remote_uuid = MICS_UUID_STR, + + .device_probe = micp_probe, + .device_remove = micp_remove, + + .accept = micp_accept, + .connect = micp_connect, + .disconnect = micp_disconnect, + + .adapter_probe = micp_server_probe, + .adapter_remove = micp_server_remove, +}; + +static unsigned int micp_id; + +static int micp_init(void) +{ + if (!(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL)) { + warn("D-Bus experimental not enabled"); + return -ENOTSUP; + } + + btd_profile_register(&micp_profile); + micp_id = bt_micp_register(micp_attached, micp_detached, NULL); + + return 0; +} + +static void micp_exit(void) +{ + if (g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL) { + btd_profile_unregister(&micp_profile); + bt_micp_unregister(micp_id); + } +} + +BLUETOOTH_PLUGIN_DEFINE(micp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, + micp_init, micp_exit) diff --git a/src/shared/micp.c b/src/shared/micp.c new file mode 100644 index 000000000..25ffa6940 --- /dev/null +++ b/src/shared/micp.c @@ -0,0 +1,922 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2023 NXP Semiconductors. All rights reserved. + * + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "lib/bluetooth.h" +#include "lib/uuid.h" + +#include "src/shared/queue.h" +#include "src/shared/util.h" +#include "src/shared/timeout.h" +#include "src/shared/att.h" +#include "src/shared/gatt-db.h" +#include "src/shared/gatt-server.h" +#include "src/shared/gatt-helpers.h" +#include "src/shared/micp.h" + +#define DBG(_micp, fmt, arg...) \ + micp_debug(_micp, "%s:%s() " fmt, __FILE__, __func__, ##arg) + +/* Application error codes */ +#define MICP_ERROR_MUTE_DISABLED 0x80 +#define MICP_ERROR_VALUE_NOT_ALLOWED 0x13 +#define BT_ATT_ERROR_OPCODE_NOT_SUPPORTED 0x81 + +/* Mute char values */ +#define MICS_NOT_MUTED 0x00 +#define MICS_MUTED 0x01 +#define MICS_DISABLED 0x02 + +struct bt_micp_db { + struct gatt_db *db; + struct bt_mics *mics; +}; + +struct bt_mics { + struct bt_micp_db *mdb; + uint8_t mute_stat; + struct gatt_db_attribute *service; + struct gatt_db_attribute *ms; + struct gatt_db_attribute *ms_ccc; +}; + +struct bt_micp { + int ref_count; + struct bt_micp_db *ldb; + struct bt_micp_db *rdb; + struct bt_gatt_client *client; + struct bt_att *att; + unsigned int mute_id; + + unsigned int idle_id; + uint8_t mute; + + struct queue *notify; + struct queue *pending; + struct queue *ready_cbs; + + bt_micp_debug_func_t debug_func; + bt_micp_destroy_func_t debug_destroy; + + void *debug_data; + void *user_data; +}; + +static struct queue *micp_db; +static struct queue *micp_cbs; +static struct queue *sessions; + +struct bt_micp_cb { + unsigned int id; + bt_micp_func_t attached; + bt_micp_func_t detached; + void *user_data; +}; + +typedef void (*micp_func_t)(struct bt_micp *micp, bool success, + uint8_t att_ecode, const uint8_t *value, + uint16_t length, void *user_data); + +struct bt_micp_pending { + unsigned int id; + struct bt_micp *micp; + micp_func_t func; + void *userdata; +}; + +struct bt_micp_ready { + unsigned int id; + bt_micp_ready_func_t func; + bt_micp_destroy_func_t destroy; + void *data; +}; + +typedef void (*micp_notify_t)(struct bt_micp *micp, uint16_t value_handle, + const uint8_t *value, uint16_t length, + void *user_data); + +struct bt_micp_notify { + unsigned int id; + struct bt_micp *micp; + micp_notify_t func; + void *user_data; +}; + +static void *iov_pull_mem(struct iovec *iov, size_t len) +{ + void *data = iov->iov_base; + + if (iov->iov_len < len) + return NULL; + + iov->iov_base += len; + iov->iov_len -= len; + + return data; +} + +static struct bt_micp_db *micp_get_mdb(struct bt_micp *micp) +{ + if (!micp) + return NULL; + + if (micp->ldb) + return micp->ldb; + + return NULL; +} + +static uint8_t *mdb_get_mute_state(struct bt_micp_db *vdb) +{ + if (!vdb->mics) + return NULL; + + return &(vdb->mics->mute_stat); +} + +static struct bt_mics *micp_get_mics(struct bt_micp *micp) +{ + if (!micp) + return NULL; + + if (micp->rdb->mics) + return micp->rdb->mics; + + micp->rdb->mics = new0(struct bt_mics, 1); + micp->rdb->mics->mdb = micp->rdb; + + return micp->rdb->mics; +} + +static void micp_detached(void *data, void *user_data) +{ + struct bt_micp_cb *cb = data; + struct bt_micp *micp = user_data; + + cb->detached(micp, cb->user_data); +} + +void bt_micp_detach(struct bt_micp *micp) +{ + if (!queue_remove(sessions, micp)) + return; + + bt_gatt_client_unref(micp->client); + micp->client = NULL; + + queue_foreach(micp_cbs, micp_detached, micp); +} + +static void micp_db_free(void *data) +{ + struct bt_micp_db *mdb = data; + + if (!mdb) + return; + + gatt_db_unref(mdb->db); + + free(mdb->mics); + free(mdb); +} + +static void micp_ready_free(void *data) +{ + struct bt_micp_ready *ready = data; + + if (ready->destroy) + ready->destroy(ready->data); + + free(ready); +} + +static void micp_free(void *data) +{ + struct bt_micp *micp = data; + + bt_micp_detach(micp); + + micp_db_free(micp->rdb); + + queue_destroy(micp->pending, NULL); + queue_destroy(micp->ready_cbs, micp_ready_free); + + free(micp); +} + +bool bt_micp_set_user_data(struct bt_micp *micp, void *user_data) +{ + + if (!micp) + return false; + + micp->user_data = user_data; + + return true; +} + +static bool micp_db_match(const void *data, const void *match_data) +{ + const struct bt_micp_db *mdb = data; + const struct gatt_db *db = match_data; + + return (mdb->db == db); +} + +struct bt_att *bt_micp_get_att(struct bt_micp *micp) +{ + if (!micp) + return NULL; + + if (micp->att) + return micp->att; + + return bt_gatt_client_get_att(micp->client); +} + +struct bt_micp *bt_micp_ref(struct bt_micp *micp) +{ + if (!micp) + return NULL; + + __sync_fetch_and_add(&micp->ref_count, 1); + + return micp; +} + +void bt_micp_unref(struct bt_micp *micp) +{ + if (!micp) + return; + + if (__sync_sub_and_fetch(&micp->ref_count, 1)) + return; + + micp_free(micp); +} + +static void micp_debug(struct bt_micp *micp, const char *format, ...) +{ + va_list ap; + + if (!micp || !format || !micp->debug_func) + return; + + va_start(ap, format); + util_debug_va(micp->debug_func, micp->debug_data, format, ap); + va_end(ap); +} + +static void micp_disconnected(int err, void *user_data) +{ + struct bt_micp *micp = user_data; + + DBG(micp, "micp %p disconnected err %d", micp, err); + + bt_micp_detach(micp); +} + +static struct bt_micp *micp_get_session(struct bt_att *att, struct gatt_db *db) +{ + const struct queue_entry *entry; + struct bt_micp *micp; + + for (entry = queue_get_entries(sessions); entry; entry = entry->next) { + struct bt_micp *micp = entry->data; + + if (att == bt_micp_get_att(micp)) + return micp; + } + + micp = bt_micp_new(db, NULL); + micp->att = att; + + bt_att_register_disconnect(att, micp_disconnected, micp, NULL); + + bt_micp_attach(micp, NULL); + + return micp; +} + +static void mics_mute_read(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct bt_mics *mics = user_data; + struct iovec iov; + + iov.iov_base = &mics->mute_stat; + iov.iov_len = sizeof(mics->mute_stat); + + gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, + iov.iov_len); +} + +static uint8_t mics_not_muted(struct bt_mics *mics, struct bt_micp *micp, + struct iovec *iov) +{ + struct bt_micp_db *mdb; + uint8_t *mute_state; + + DBG(micp, "Mute state OP: Not Muted"); + + mdb = micp_get_mdb(micp); + if (!mdb) { + DBG(micp, "error: MDB not available"); + return 0; + } + + mute_state = mdb_get_mute_state(mdb); + if (!mute_state) { + DBG(micp, "Error : Mute State not available"); + return 0; + } + + *mute_state = MICS_NOT_MUTED; + + gatt_db_attribute_notify(mdb->mics->ms, (void *)mute_state, + sizeof(uint8_t), bt_micp_get_att(micp)); + + return 0; +} + +static uint8_t mics_muted(struct bt_mics *mics, struct bt_micp *micp, + struct iovec *iov) +{ + struct bt_micp_db *mdb; + uint8_t *mute_state; + + DBG(micp, "Mute state OP: Muted"); + + mdb = micp_get_mdb(micp); + if (!mdb) { + DBG(micp, "error: MDB not available"); + return 0; + } + + mute_state = mdb_get_mute_state(mdb); + + *mute_state = MICS_MUTED; + + gatt_db_attribute_notify(mdb->mics->ms, (void *)mute_state, + sizeof(uint8_t), bt_micp_get_att(micp)); + + return 0; +} + +#define MICS_OP(_str, _op, _size, _func) \ + { \ + .str = _str, \ + .op = _op, \ + .size = _size, \ + .func = _func, \ + } + +struct mics_op_handler { + const char *str; + uint8_t op; + size_t size; + uint8_t (*func)(struct bt_mics *mics, struct bt_micp *micp, + struct iovec *iov); +} micp_handlers[] = { + MICS_OP("Not Muted", MICS_NOT_MUTED, + sizeof(uint8_t), mics_not_muted), + MICS_OP("Muted", MICS_MUTED, + sizeof(uint8_t), mics_muted), + {}}; + +static void mics_mute_write(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + const uint8_t *value, size_t len, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct bt_mics *mics = user_data; + struct bt_micp *micp = micp_get_session(att, mics->mdb->db); + struct iovec iov = { + .iov_base = (void *)value, + .iov_len = len, + }; + uint8_t *micp_op, *mute_state; + struct mics_op_handler *handler; + uint8_t ret = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; + struct bt_micp_db *mdb; + + DBG(micp, "MICS Mute Char write: len: %ld: %ld", len, iov.iov_len); + + if (offset) { + DBG(micp, "invalid offset: %d", offset); + ret = BT_ATT_ERROR_INVALID_OFFSET; + goto respond; + } + + if (len < sizeof(*micp_op)) { + DBG(micp, "invalid length: %ld < %ld sizeof(param)", len, + sizeof(*micp_op)); + ret = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; + goto respond; + } + + micp_op = iov_pull_mem(&iov, sizeof(*micp_op)); + + if ((*micp_op == MICS_DISABLED) || (*micp_op != MICS_NOT_MUTED + && *micp_op != MICS_MUTED)) { + DBG(micp, "Invalid operation - MICS DISABLED/RFU mics op:%d", + micp_op); + ret = MICP_ERROR_VALUE_NOT_ALLOWED; + goto respond; + } + + mdb = micp_get_mdb(micp); + if (!mdb) { + DBG(micp, "error: MDB not available"); + goto respond; + } + + mute_state = mdb_get_mute_state(mdb); + if (*mute_state == MICS_DISABLED) { + DBG(micp, "state: MICS DISABLED , can not write value: %d", + *micp_op); + ret = MICP_ERROR_MUTE_DISABLED; + goto respond; + } + + for (handler = micp_handlers; handler && handler->str; handler++) { + DBG(micp, "handler->op: %d micp_op: %d iov.iov_len: %ld", + handler->op, *micp_op, iov.iov_len); + if (handler->op != *micp_op) + continue; + + if (len < handler->size) { + DBG(micp, "invalid len %ld : %ld < %ld handler->size", + len, iov.iov_len, handler->size); + ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED; + goto respond; + } + + break; + } + + if (handler && handler->str) { + DBG(micp, "%s", handler->str); + + ret = handler->func(mics, micp, &iov); + } else { + DBG(micp, "unknown opcode 0x%02x", *micp_op); + ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED; + } + +respond: + gatt_db_attribute_write_result(attrib, id, ret); +} + +static struct bt_mics *mics_new(struct gatt_db *db) +{ + struct bt_mics *mics; + bt_uuid_t uuid; + + if (!db) + return NULL; + + mics = new0(struct bt_mics, 1); + + mics->mute_stat = MICS_MUTED; + + /* Populate DB with MICS attributes */ + bt_uuid16_create(&uuid, MICS_UUID); + mics->service = gatt_db_add_service(db, &uuid, true, 4); + + bt_uuid16_create(&uuid, MUTE_CHRC_UUID); + mics->ms = gatt_db_service_add_characteristic(mics->service, + &uuid, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, + BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE + | BT_GATT_CHRC_PROP_NOTIFY, + mics_mute_read, mics_mute_write, + mics); + + mics->ms_ccc = gatt_db_service_add_ccc(mics->service, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); + + gatt_db_service_set_active(mics->service, true); + + return mics; +} + +static struct bt_micp_db *micp_db_new(struct gatt_db *db) +{ + struct bt_micp_db *mdb; + + if (!db) + return NULL; + + mdb = new0(struct bt_micp_db, 1); + mdb->db = gatt_db_ref(db); + + if (!micp_db) + micp_db = queue_new(); + + mdb->mics = mics_new(db); + mdb->mics->mdb = mdb; + + queue_push_tail(micp_db, mdb); + + return mdb; +} + +static struct bt_micp_db *micp_get_db(struct gatt_db *db) +{ + struct bt_micp_db *mdb; + + mdb = queue_find(micp_db, micp_db_match, db); + if (mdb) + return mdb; + + return micp_db_new(db); +} + +void bt_micp_add_db(struct gatt_db *db) +{ + micp_db_new(db); +} + +bool bt_micp_set_debug(struct bt_micp *micp, bt_micp_debug_func_t func, + void *user_data, bt_micp_destroy_func_t destroy) +{ + if (!micp) + return false; + + if (micp->debug_destroy) + micp->debug_destroy(micp->debug_data); + + micp->debug_func = func; + micp->debug_destroy = destroy; + micp->debug_data = user_data; + + return true; +} + +unsigned int bt_micp_register(bt_micp_func_t attached, bt_micp_func_t detached, + void *user_data) +{ + struct bt_micp_cb *cb; + static unsigned int id; + + if (!attached && !detached) + return 0; + + if (!micp_cbs) + micp_cbs = queue_new(); + + cb = new0(struct bt_micp_cb, 1); + cb->id = ++id ? id : ++id; + cb->attached = attached; + cb->detached = detached; + cb->user_data = user_data; + + queue_push_tail(micp_cbs, cb); + + return cb->id; +} + +static bool match_id(const void *data, const void *match_data) +{ + const struct bt_micp_cb *cb = data; + unsigned int id = PTR_TO_UINT(match_data); + + return (cb->id == id); +} + +bool bt_micp_unregister(unsigned int id) +{ + struct bt_micp_cb *cb; + + cb = queue_remove_if(micp_cbs, match_id, UINT_TO_PTR(id)); + if (!cb) + return false; + + free(cb); + + return true; +} + +struct bt_micp *bt_micp_new(struct gatt_db *ldb, struct gatt_db *rdb) +{ + struct bt_micp *micp; + struct bt_micp_db *mdb; + + if (!ldb) + return NULL; + + mdb = micp_get_db(ldb); + if (!mdb) + return NULL; + + micp = new0(struct bt_micp, 1); + micp->ldb = mdb; + micp->pending = queue_new(); + micp->ready_cbs = queue_new(); + + if (!rdb) + goto done; + + mdb = new0(struct bt_micp_db, 1); + mdb->db = gatt_db_ref(rdb); + + micp->rdb = mdb; + +done: + bt_micp_ref(micp); + + return micp; +} + +static void micp_pending_destroy(void *data) +{ + struct bt_micp_pending *pending = data; + struct bt_micp *micp = pending->micp; + + if (queue_remove_if(micp->pending, NULL, pending)) + free(pending); +} + +static void micp_pending_complete(bool success, uint8_t att_ecode, + const uint8_t *value, uint16_t length, + void *user_data) +{ + struct bt_micp_pending *pending = user_data; + + if (pending->func) + pending->func(pending->micp, success, att_ecode, value, length, + pending->userdata); +} + +static void micp_read_value(struct bt_micp *micp, uint16_t value_handle, + micp_func_t func, void *user_data) +{ + struct bt_micp_pending *pending; + + pending = new0(struct bt_micp_pending, 1); + pending->micp = micp; + pending->func = func; + pending->userdata = user_data; + + pending->id = bt_gatt_client_read_value(micp->client, value_handle, + micp_pending_complete, pending, + micp_pending_destroy); + + if (!pending->id) { + DBG(micp, "unable to send read request"); + free(pending); + return; + } + + queue_push_tail(micp->pending, pending); +} + +static void micp_register(uint16_t att_ecode, void *user_data) +{ + struct bt_micp_notify *notify = user_data; + + if (att_ecode) + DBG(notify->micp, "MICP register failed 0x%04x", att_ecode); +} + +static void micp_notify(uint16_t value_handle, const uint8_t *value, + uint16_t length, void *user_data) +{ + struct bt_micp_notify *notify = user_data; + + if (notify->func) + notify->func(notify->micp, value_handle, value, length, + notify->user_data); +} + +static void micp_notify_destroy(void *data) +{ + struct bt_micp_notify *notify = data; + struct bt_micp *micp = notify->micp; + + if (queue_remove_if(micp->notify, NULL, notify)) + free(notify); +} + +static unsigned int micp_register_notify(struct bt_micp *micp, + uint16_t value_handle, + micp_notify_t func, + void *user_data) +{ + struct bt_micp_notify *notify; + + notify = new0(struct bt_micp_notify, 1); + notify->micp = micp; + notify->func = func; + notify->user_data = user_data; + + notify->id = bt_gatt_client_register_notify(micp->client, + value_handle, micp_register, + micp_notify, notify, + micp_notify_destroy); + if (!notify->id) { + DBG(micp, "Unable to register for notifications"); + free(notify); + return 0; + } + + queue_push_tail(micp->notify, notify); + + return notify->id; +} + +static void micp_mute_state_notify(struct bt_micp *micp, uint16_t value_handle, + const uint8_t *value, uint16_t length, + void *user_data) +{ + uint8_t mute_state; + + memcpy(&mute_state, value, sizeof(mute_state)); + + DBG(micp, "Mute state: 0x%x", mute_state); +} + +static void read_mute_state(struct bt_micp *micp, bool success, + uint8_t att_ecode, const uint8_t *value, + uint16_t length, void *user_data) +{ + uint8_t *mute_state; + struct iovec iov = { + .iov_base = (void *)value, + .iov_len = length, + }; + + if (!success) { + DBG(micp, "Unable to read Mute state: error 0x%02x", att_ecode); + return; + } + + mute_state = iov_pull_mem(&iov, sizeof(uint8_t)); + if (mute_state == NULL) { + DBG(micp, "Unable to get Mute state"); + return; + } + + DBG(micp, "Mute state: %x", *mute_state); +} + +static void foreach_mics_char(struct gatt_db_attribute *attr, void *user_data) +{ + struct bt_micp *micp = user_data; + uint16_t value_handle; + bt_uuid_t uuid, uuid_mute; + struct bt_mics *mics; + + if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, + NULL, NULL, &uuid)) + return; + + bt_uuid16_create(&uuid_mute, MUTE_CHRC_UUID); + if (!bt_uuid_cmp(&uuid, &uuid_mute)) { + DBG(micp, "MICS Mute characteristic found: handle 0x%04x", + value_handle); + + mics = micp_get_mics(micp); + if (!mics || mics->ms) + return; + + mics->ms = attr; + + micp_read_value(micp, value_handle, read_mute_state, micp); + + micp->mute_id = micp_register_notify(micp, value_handle, + micp_mute_state_notify, NULL); + } +} + +static void foreach_mics_service(struct gatt_db_attribute *attr, + void *user_data) +{ + struct bt_micp *micp = user_data; + struct bt_mics *mics = micp_get_mics(micp); + + mics->service = attr; + + gatt_db_service_set_claimed(attr, true); + gatt_db_service_foreach_char(attr, foreach_mics_char, micp); +} + +unsigned int bt_micp_ready_register(struct bt_micp *micp, + bt_micp_ready_func_t func, void *user_data, + bt_micp_destroy_func_t destroy) +{ + struct bt_micp_ready *ready; + static unsigned int id; + + DBG(micp, "bt_micp_ready_register_Entry\n"); + if (!micp) + return 0; + + ready = new0(struct bt_micp_ready, 1); + ready->id = ++id ? id : ++id; + ready->func = func; + ready->destroy = destroy; + ready->data = user_data; + + queue_push_tail(micp->ready_cbs, ready); + + return ready->id; +} + +static bool match_ready_id(const void *data, const void *match_data) +{ + const struct bt_micp_ready *ready = data; + unsigned int id = PTR_TO_UINT(match_data); + + return (ready->id == id); +} + +bool bt_micp_ready_unregister(struct bt_micp *micp, unsigned int id) +{ + struct bt_micp_ready *ready; + + ready = queue_remove_if(micp->ready_cbs, match_ready_id, + UINT_TO_PTR(id)); + if (!ready) + return false; + + micp_ready_free(ready); + + return true; +} + +static struct bt_micp *bt_micp_ref_safe(struct bt_micp *micp) +{ + if (!micp || !micp->ref_count) + return NULL; + + return bt_micp_ref(micp); +} + +static void micp_notify_ready(struct bt_micp *micp) +{ + const struct queue_entry *entry; + + if (!bt_micp_ref_safe(micp)) + return; + + for (entry = queue_get_entries(micp->ready_cbs); entry; + entry = entry->next) { + struct bt_micp_ready *ready = entry->data; + + ready->func(micp, ready->data); + } + + bt_micp_unref(micp); +} + +static void micp_idle(void *data) +{ + struct bt_micp *micp = data; + + micp->idle_id = 0; + micp_notify_ready(micp); +} + +bool bt_micp_attach(struct bt_micp *micp, struct bt_gatt_client *client) +{ + bt_uuid_t uuid; + + if (!sessions) + sessions = queue_new(); + + queue_push_tail(sessions, micp); + + if (!client) + return true; + + if (micp->client) + return false; + + micp->client = bt_gatt_client_clone(client); + if (!micp->client) + return false; + + bt_gatt_client_idle_register(micp->client, micp_idle, micp, NULL); + + bt_uuid16_create(&uuid, MICS_UUID); + gatt_db_foreach_service(micp->ldb->db, &uuid, foreach_mics_service, + micp); + return true; +} diff --git a/src/shared/micp.h b/src/shared/micp.h new file mode 100644 index 000000000..b307ac9f4 --- /dev/null +++ b/src/shared/micp.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2023 NXP Semiconductors. All rights reserved. + * + */ +#include +#include + +#include "src/shared/io.h" +#include "src/shared/gatt-client.h" + +struct bt_micp; + +typedef void (*bt_micp_ready_func_t)(struct bt_micp *micp, void *user_data); +typedef void (*bt_micp_destroy_func_t)(void *user_data); +typedef void (*bt_micp_debug_func_t)(const char *str, void *user_data); +typedef void (*bt_micp_func_t)(struct bt_micp *micp, void *user_data); + +struct bt_micp *bt_micp_ref(struct bt_micp *micp); +void bt_micp_unref(struct bt_micp *micp); + +void bt_micp_add_db(struct gatt_db *db); + +bool bt_micp_attach(struct bt_micp *micp, struct bt_gatt_client *client); +void bt_micp_detach(struct bt_micp *micp); + +bool bt_micp_set_debug(struct bt_micp *micp, bt_micp_debug_func_t func, + void *user_data, bt_micp_destroy_func_t destroy); + +struct bt_att *bt_micp_get_att(struct bt_micp *micp); + +bool bt_micp_set_user_data(struct bt_micp *micp, void *user_data); + +/* session related functions */ +unsigned int bt_micp_register(bt_micp_func_t attached, bt_micp_func_t detached, + void *user_data); +unsigned int bt_micp_ready_register(struct bt_micp *micp, + bt_micp_ready_func_t func, void *user_data, + bt_micp_destroy_func_t destroy); +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); From patchwork Thu Aug 3 07:20:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mahesh Talewad X-Patchwork-Id: 711006 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AE5B6C001E0 for ; Thu, 3 Aug 2023 07:21:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233785AbjHCHVE (ORCPT ); Thu, 3 Aug 2023 03:21:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38780 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233792AbjHCHVA (ORCPT ); Thu, 3 Aug 2023 03:21:00 -0400 Received: from EUR04-DB3-obe.outbound.protection.outlook.com (mail-db3eur04on2082.outbound.protection.outlook.com [40.107.6.82]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E8A992D69 for ; Thu, 3 Aug 2023 00:20:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=mw8nSbtQJ1j9/0de/ficGnBOyyVbRqugh65eKpSBa1/q/L95acwxnc8pNroeEEIzfvmexB4ooYV+gODtU7TsznpO9ozsmqmjMr0RoGSjLgOoHgSpQRrAyACb6I9TkF5eX8eBrkLM2qV4obCxhq2cXm79wSxeuv9E0PsW17HrKOVAF42nGKJvxzmAuZMWS6ln+qrlUoBZckCr73Ff0ikkyrjsEg0kDUzK+KYQHUTvY0mU5zX5LekLc32wPshXkaO6EIoW3O3eFD6cCt7aLLwi1aS4kla4QoHXjoZZqjpv4GE5bCivkIdBjD7asoi0DQIR0WOaQQrerI3pB+qB5IQoGQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=SIX4M8oYNUY2aGL5/KwkyU0WRb/cYKNuwctDzxFRIck=; b=b/bQOcPJ51/9x8bCwv9VXnH6mnJhJNPBsiTaQnynFhkzphijFYOV2SfPANR0EDl2DdT8a/xN6AqrUnFscl/emfIUc0rqXnSt+McLmzsLyMOmjgPYzgwXfIxKmqSCTS7rV/2kzKON8poP0TbQUsbfNQMhtRxSqhoE89H3c69mc+zFvti3nh+3yU5cwry5bCnyArDq09iAAYIfTHAhDwlCShh8679RbcV/lXRmIbPNwhC+HMvlQTjIRRc3yOV7oHttxNpc1k37XmPZv0In58QVZrRf0n6HGx1yLDQS3dpANbP86qPh0moxJNtbE9NIckHhd+pk3RhIfu+JefsE0RpuQw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=SIX4M8oYNUY2aGL5/KwkyU0WRb/cYKNuwctDzxFRIck=; b=BALd7WQmu01MakblyENxxhyyC30OXLYaENmgER3Ve3+uCXnI5uV1olsHEZ6hbVGiNY+PSnjbvxk9iYVOvJ+xH8GYjF9v9imEDksgw0CuU34K0fOhsMgzZ18z18nWkB7DtvyrBXeUzfyW57+ozMhcipIV4+0AWJkEn2UVF9c8bV0= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from VI1PR04MB6991.eurprd04.prod.outlook.com (2603:10a6:803:12d::13) by DU0PR04MB9418.eurprd04.prod.outlook.com (2603:10a6:10:359::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6631.47; Thu, 3 Aug 2023 07:20:55 +0000 Received: from VI1PR04MB6991.eurprd04.prod.outlook.com ([fe80::4299:5db1:461:bc17]) by VI1PR04MB6991.eurprd04.prod.outlook.com ([fe80::4299:5db1:461:bc17%7]) with mapi id 15.20.6631.046; Thu, 3 Aug 2023 07:20:55 +0000 From: Mahesh Talewad To: linux-bluetooth@vger.kernel.org Cc: luiz.dentz@gmail.com, devyani.godbole@nxp.com, nitin.jadhav@nxp.com, mahesh.talewad@nxp.com Subject: [PATCH BlueZ v1 2/3] client/player: Add interface menu to configure MICP-MICS during PTS testing. Date: Thu, 3 Aug 2023 10:20:10 +0300 Message-Id: <20230803072011.191449-3-mahesh.talewad@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230803072011.191449-1-mahesh.talewad@nxp.com> References: <20230803072011.191449-1-mahesh.talewad@nxp.com> X-ClientProxiedBy: SG2PR01CA0125.apcprd01.prod.exchangelabs.com (2603:1096:4:40::29) To VI1PR04MB6991.eurprd04.prod.outlook.com (2603:10a6:803:12d::13) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: VI1PR04MB6991:EE_|DU0PR04MB9418:EE_ X-MS-Office365-Filtering-Correlation-Id: dc57af45-d4fb-4299-8575-08db93f22d59 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: uGmgiJglJvdoOVHaHYYmNtYtQJX/bzg1BdQ7ZHEDlaTVU/ZeiNm8CD0fG6ihR2m5X/NwxTYC/mxTeZNLjKYNIAxSav2vJydBwxp9/WlUj/pL68ucraB7HJ62pyggGa6RSuaN5H0OHO2quhTPJjf3Ws17QSAZFYK9bZxr5Zjs2oloMFiosY7irvZ80cG/2iC/M0bexK22U+7bncdu7CZBzAsbC0ljgWp1y6CPPKbKYla9ch2DnzI9qewZv05FvC6meSj9Y4Cy5wFPFIfAqtgtTU60Lz3tgjH2rxvSIardgBJzRCXYqlPsJ+vv6jlOaua0TWCPEwJa5QHaHcKUgewz6lJ5UHJdHDhVG8RRguvkZGShbMbu7sQOG8WmbHtW+QBDBuVSxYbuEeVluoGVPQpto/pGWZeyT4pllchCjOAE9bc8dgQD+Jc5xKLnPIwlI5diDEMoX9SlA8GFDe9K+GzXN9RcIdWapgwh4J2E9+Yisn0ezFKl/NHhiNg4BrdXMcD32InBkP5d6pSh13Fpd4JvOjX7oQfVBwTbmZy0rhHaaE43sp/7Lm/74lbanYYfAlA0O7txnJ5knv3QNZwAomOBbzBxw7equ0zFzSC3Y6OCQZiMtF2ELZmNSonJRSiNYwbP X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:VI1PR04MB6991.eurprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230028)(4636009)(39860400002)(346002)(376002)(396003)(366004)(136003)(451199021)(36756003)(86362001)(52116002)(478600001)(38100700002)(38350700002)(2616005)(1076003)(186003)(26005)(83380400001)(6506007)(55236004)(41300700001)(8676002)(8936002)(44832011)(6512007)(6666004)(6486002)(30864003)(316002)(66476007)(66556008)(5660300002)(6916009)(4326008)(2906002)(66946007); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: Eqf04dOAd41I1bkjq5pKqKBCW+aGq24AwWBTKr5fNVWpX4in/tNSEwECHuFmLfaBKIII0Y4HBaJ3Y0IaOV5XD4mBa7+PsvUD9wybFlG35Vsy8tncl4NU99h4moWy9n8yGV1YoJTBj8HH1pYit6BwjHy2dgI16dbVYmxftLz+vJZgf2f8/d2GxdTTLywQrs6Xq3sJr5LlvanBPxwB0CqnWjjPull1jGu9zyU6D0RuQ+6e0LIuUSDDudhRYd93gsPtt5VgoSr6HpD+aNrMaKKmiO7xADWikcf/YsOUQzNOGcADrG5XjD3Yarvwv1LJyv9baOEMSLwIz8LB++H+NuQp1ObCJj/0ruMaes+uWKMvu+ZdHKqYQ1gnUZciQXa95P4S71hdQ9nzQy/eA8B9yZIxJjpLjKAbj9ydTy4+UWpQkWylzPJ7ijxxBbKnJRNsd0ytZGV5Wme+Gr8UNpPW070rl1v5c2b1zjQ53kXVPEkGkyrM7FCXlOz+useLkHDpjCLCFDUFtHqcYMHnp2brX44tLvMd4PaccqDCs5eiPDkRFpF9jk85xjk+zXb3bhTwsefB63n9F5VlVV5CpqoIas+Y6EcXEKrzksTgYDKGFkeuqEoeAGnoA+zismD2BsXVkj4hTWOp5NTiuqlngtd2DnpLwiOsEE6VOssVKieDUnlBndh2wOOHmCvchnu+sawDUXUbVltFXs6MUsETy4QYcIWYZhZaOFDou38fgtwSbUL1H32yY4M8fwOBSM5DvlGqYxk+R5P7rY4yvDWNkYsX6z9JfFdPy6BH7Kasx7is4AZXBXzV9lobSsKaKxu0iu9M8szT58/R1B23Eoz1h/XA+N8DNV6n7yA8Dnt9zsc022e1MuyAnWC4oEx7CYWsbNr0C9Xj+F1R+NutpepVG+0XjG3LQwitCTtDDPt5PwHguOoiUpZ0Mz2+8FNvdpHPy3msuU2sZIQ9++QFD9HLoWiHWrTJ/T0D7fLVO9uv2DPxXJqi+28JGuHKAexR71idCameA3OExy0C1Kre2R6Fa5/fKKApuDEXE/AkeOyf8MJYYMT7ErhBg48fz9LA1yD+0kWrjDDOn3QVW+P1ssIgexk+1gJoDWl2d9XgP7YIynvZt43xFwoTU71x4DGOVEj8/Muu5qs1On5ZDJverhx2wU3M1+KpHLZAEF775eZAkWUQG38bBU4g4TJ1B8eOvhTTir80Y4gpNGC9jjcn2EJRlGIc4ntpcLKMxo/G2o/DoU84p8tGLL2NTYElNroXXZEiqJmsiYPkOYz6CZb66ygkZC6+MAM0ZxBmGfL9ivDoXLJmyQHsG3SGeI1MT1cGMx4uNxC2UXtwK0nnyn7mHol+FWKbs0WOzqBjKDOgpvjOaaRQDKWuZiKTvxB9NAPoAfprnlBhFQ8FYgVX0mj3ipdqvSVOlul8x0tLHrDJsuENP6r/kG4hvfufIbxCRJFI34l+cYW5R7xBwwsD2iy+LefoYMNNaoAQwNuaPViJDZHJCR+GPtjqapjvYKCNEEGB1ot9arpsoakqGULzaUHWAcX8yJ/u/8lEAHkyvqSdNInMaB5qic31MtRKuR95a3R+H40NTgMNCmHo X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: dc57af45-d4fb-4299-8575-08db93f22d59 X-MS-Exchange-CrossTenant-AuthSource: VI1PR04MB6991.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 03 Aug 2023 07:20:55.6440 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: 1texrbndt7axO0FbSuzajGjvVOzoUfHQ0n17A9uX+JozLIdxtlhrs4bLM+0mLXl5aRfkHd8IlLTuvZqlQTrEGA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DU0PR04MB9418 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org - 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", "", cmd_show_item, "Show item information", item_generator}, +#ifdef MICP_MICS_PTS_FLAG + { "mics_mute", "", cmd_set_mute_state, + "Set Mics Mute state to on / off", + NULL }, + { "mics_state", "", cmd_enable_disable_mute_state, + "Set Mics Mute state to on[enable] / off[disable]", + NULL }, + { "micp_discover", "", cmd_micp_discover_mute, + "discover Mute Characteristic", + NULL }, + { "micp_read", "", cmd_enable_read_mute_state, + "Read Mute Characteristic", + NULL }, + { "micp_write", "", 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 +#endif + #define _GNU_SOURCE #include #include @@ -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*/ From patchwork Thu Aug 3 07:20:11 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mahesh Talewad X-Patchwork-Id: 709708 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 28A6BC001E0 for ; Thu, 3 Aug 2023 07:21:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233772AbjHCHVL (ORCPT ); Thu, 3 Aug 2023 03:21:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38880 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233697AbjHCHVJ (ORCPT ); Thu, 3 Aug 2023 03:21:09 -0400 Received: from EUR04-DB3-obe.outbound.protection.outlook.com (mail-db3eur04on2079.outbound.protection.outlook.com [40.107.6.79]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2899E2D6A for ; Thu, 3 Aug 2023 00:21:05 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=JSfVjQGjTwDfpFJJF73kzEu8y2Vc6lyrsRugxU4Q0TYM/opdWkZ/YEQfewHyhriSwUbkXn6ryTUhCKMC12yWVL337hSvleeoPOQyEViga/twlh3D0CwAKEzliscsfPUnfRfX03eM7gp8GYWXpBiE1akIEHefDcQHnNoibQGRZO9Wg3pNlNCMf6dRUWsBH/ohP6HM+8HpRPhg5rOwCaiLIuHtGg7UOrAq9lcG2/p6H0CuMJ2Z5x2jwRr3rzQy5WdUwh2mBIdsUSgXtIYRrnRdv6UZjLsj9JmDIEndOXLc8dlakCym7qIo6xf7vGFN6FKx95KZjHO7X8R42EgIHDtf7A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=ybGHPIVxTh7xjWPFbuoY4xW7eDTO+NySTP1UetPmwRI=; b=QPREXZkd1yA2AyFl+6fx941irGnnoipjrGrwdhiOfmj++3w1Ph6D2CV5+pz9O8YebE/0I3YMGT76ly+iI4oUQoDox7oHYg0xa07LqgVVrSkLttDI7qQyU0lX/t7DIfuAVeTAMNtixEi0SIfrxLaSbsUPkg54CduCTY55eG5SNXeYSpwUfsxBGWcQigfiaErrCVezA/yN/vdFETIKmlt3Sr3qepWsUbxvOkNnu5wnJX05SKUCeDUzcUexyEmQAZGxSk+yL4aKSJuT6ePolcQtKogSsKvKhH8+Ox6IpZj5MDUutQ9VcY+sNgyopcNldPMwNCt4TqEpv2vcqVhr0faaGA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=ybGHPIVxTh7xjWPFbuoY4xW7eDTO+NySTP1UetPmwRI=; b=syeACVZ2zr6S7MxgCfEv3bwuZtSJX6PcneBjxFy6Q6pa5Kn0o6oH6k4n3ih/0KioTOuIPdnIRNJMQXipwWhoQowVVcYW20cF1qZ08EG+qa+OwoIY3IYOEW1X2lXnGqDNl0SA11fDGRawrB+J/fp+K12VW+0SNXrK4tk8AmQX01s= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from VI1PR04MB6991.eurprd04.prod.outlook.com (2603:10a6:803:12d::13) by DU0PR04MB9418.eurprd04.prod.outlook.com (2603:10a6:10:359::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6631.47; Thu, 3 Aug 2023 07:21:02 +0000 Received: from VI1PR04MB6991.eurprd04.prod.outlook.com ([fe80::4299:5db1:461:bc17]) by VI1PR04MB6991.eurprd04.prod.outlook.com ([fe80::4299:5db1:461:bc17%7]) with mapi id 15.20.6631.046; Thu, 3 Aug 2023 07:21:02 +0000 From: Mahesh Talewad To: linux-bluetooth@vger.kernel.org Cc: luiz.dentz@gmail.com, devyani.godbole@nxp.com, nitin.jadhav@nxp.com, mahesh.talewad@nxp.com Subject: [PATCH BlueZ v1 3/3] unit/test-micp: Add unit test code for testing MICP-MICS Date: Thu, 3 Aug 2023 10:20:11 +0300 Message-Id: <20230803072011.191449-4-mahesh.talewad@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230803072011.191449-1-mahesh.talewad@nxp.com> References: <20230803072011.191449-1-mahesh.talewad@nxp.com> X-ClientProxiedBy: SG2PR01CA0125.apcprd01.prod.exchangelabs.com (2603:1096:4:40::29) To VI1PR04MB6991.eurprd04.prod.outlook.com (2603:10a6:803:12d::13) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: VI1PR04MB6991:EE_|DU0PR04MB9418:EE_ X-MS-Office365-Filtering-Correlation-Id: 4bc39ef1-8487-451c-ec80-08db93f2313d X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: evl8h23So9/UIXHmtMfUtZksjRBXERGPNZXbnqXAd/A9nz+KY+K+7csUIBnmEZ9dMnajqiiTpRlBihpDKNSUHAUOOVw6881mfVMGFmOORFrNF88oZclqZfMEU7StH1UBVvPEk9bEz3T/BaA4S2ntmFSK5GNTHa6HQGTeZ1sqzw8c6d1qLZyyZNT4PS5k7JgPUhL81ASCEH8+JjrUWg5Iv8xdrvpGwCzI1MIexxMmg1SqEwII8j2vZwoq4mwVKJgiqWSRhRQjTCCZeiio2uOeEWZgeLSmjLtdV6SHiGuvcdzsInRsbKkKGtNDMCC4S+Ye0fFyS0j1bYUIjR0CJuHXabSeO56cXbfFkMCNURqvbhcDx0SFYTOyjgwa6/2Sfq0xzIqoLVuWbnajK0Nf0Qld4UiBI7qCEYfbQX/cWZUrTrcuQv+p55vsKl+tfUOzRJjtLDGTXfXH5Jenn/k8/Que3sgs1yZoBi/LmwofgrnKe146C/Dc4/2uNl9BRbxKDIqDNeSwrYFDYerOrlmPcBO0577lDzCGz2LOvTl35mOhdC4nIDGVrXyjzoK24hK9njKXZ7mVHETjPcIplE+becd1m5/bYJOnpy3DqjkNn9ysbkCxhWTcfzri+osyQjIFmszB X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:VI1PR04MB6991.eurprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230028)(4636009)(39860400002)(346002)(376002)(396003)(366004)(136003)(451199021)(36756003)(86362001)(52116002)(478600001)(38100700002)(38350700002)(2616005)(1076003)(186003)(26005)(83380400001)(6506007)(55236004)(41300700001)(8676002)(8936002)(44832011)(6512007)(6666004)(6486002)(30864003)(316002)(66476007)(66556008)(5660300002)(6916009)(4326008)(2906002)(66946007); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: Om1tjF1Xd6X6dBMDaGYkt3yjawvh+DSjhb+qOUPo2LTleUPQ/OGnJQGz7Kr19aLAKy+VksmJYGs/Eb4sQbLcBqQehao7FuzlM1CbkCTfP2so/KwWCh2u5iO8or/MejQvoMPHf6k3Fe9AHKc8yY2VL0i6zJowfQP3RGadqnSQqkbbSl7hnc4obWonLusQTs0UbuQ9VnWGk3kQyJktfYPLy3pwFyRD9nZaVN1TqAeUwrIi7710wV6R/eFGgVrA0DWaVKFK1HFiYFei5UDH8NoSh5JqW5UZouy3QtROMR9glatJ1hMctc/J2dMhz30d53pqlMzLc1af/rwLSbePXikdnX99XS4US4spV4zkxyLNghJbB16EUTcPkutXHjrixxExyRHqlaya+JWpJtxtQgeJY6fiuPQY087R4MU1JLSSxX8JVsnkgzpwS5+4TD0ZOuyNCAznxt6gyu+dIpIRMVFEpfr/Fc4WOxk1CU1jKLa1E1E6AeO5r2ScQR7dk8pmMKrAKO8oRKOha18ZYC2Ds1gQ05V1Pr8zUZ0eHdJ0WGKH1EQ/c/VyR0Qr+wfo2JIePkFSvR5fmKRKVesXX+rQVOgbDWMBZqGD6XSV3u/yFNEKQmvvJsK4UGhetsxHKI/r1ysQh05UnvDOiy4uejKfUG2+yZwTpiDAhWftUmx3ceyBdHYXw9SAEE2MGAy2GFRyeKEUSoFRcru/5/MimM13Rv3kV3wsOyOlTPFIfN4Pf99x0JdrnIZiGGXg5Ns9JrCtNM0pdxV2wl53kDU+7ZUEYJQT198EWDYDTm8yXCLrnriB/Ww/qIgO0l5/2om7pVlLlXk1YVP9CRxGnXZMU0xejYX+VEMcY4kNKW0X4d50oleg6q9+qb95UPq5VGe2u+tFWHv6peFOk+S84OS+6CPALx2slt4j/QQJPcJNa1JKHYd44yeNDclnDRU+SwJ9oz0XWH3b0PdfAu/l3+h0ly40V0AXpEY41AnEPdBXWbFkckUpj0hF5epvF81oL2DoCttmvAq+BHfpBYqje4udFv1BhRKmiY3GqmZtW+v7lszYuy5L2KcNqmoOkuOCjxLFuq66xq6jly4c5YwXu6S5/0P5K3/swWe43uJrkK7hqRe+p2TY36BYzmqqL/iQZOPjetaQ4sfvW1mQXO+q+u5IK1b+jDf02t/yllE00h0/kc7sbfDgNpEETQd3HdwsgMbdFJjVTQK6GIs6lUAE/Tv94XOTnovCCwj4rT5BK1Ppx0y9v2zfx0pOSbnVg0H/s4+VeiQQiSWHAGx3MXRQWzlTruLtC6mnzKegMjMBVRreTq3qh0h7omcCOP5Ekc4qN4Ky0oKOJwQC8vXSphsQtRzbjGqjHkAbYuGxtx3H3Ja5fPtwyxO7amlGkp/UWR1thhcvmruITu5canogl+lw/A5SUmg4pmHiFRJc4FScR8fXV1T3v+tFKlpjxqpM2aq+pxyUpu7vbiVr12AgcGir+NkcHQwbyHkikLea2YFa9yGDyEaqroC8OCdYR0KvRTn8jx9kmhdjG8j9gMeflToClzdzYS43ZOzeCPZrMeF9owE5Xbl7xMZx8kNPaH2TMXc76/xIhQYMufob X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 4bc39ef1-8487-451c-ec80-08db93f2313d X-MS-Exchange-CrossTenant-AuthSource: VI1PR04MB6991.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 03 Aug 2023 07:21:02.2513 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: DsZ2l6tOvTaQC1GGeMcb5H9B3DMKCF1Kz64BF62JbQwVsmrFGeCkQZ2x7wy9BUa3dasvTe8GFHCiW2M74dmSmA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DU0PR04MB9418 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org -Implemented UNIT Testcases for MICS[test-micp.c] and MICP[test-mics.c]. --- Makefile.am | 13 ++ src/shared/micp.c | 26 ++++ src/shared/micp.h | 2 + unit/test-micp.c | 357 ++++++++++++++++++++++++++++++++++++++++++++++ unit/test-mics.c | 290 +++++++++++++++++++++++++++++++++++++ 5 files changed, 688 insertions(+) create mode 100644 unit/test-micp.c create mode 100644 unit/test-mics.c 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/src/shared/micp.c b/src/shared/micp.c index c5b814d98..bb71999be 100644 --- a/src/shared/micp.c +++ b/src/shared/micp.c @@ -1042,3 +1042,29 @@ void micp_char_write_value(uint16_t handle) } #endif /*MICP_MICS_PTS_FLAG*/ + +static void micp_write_cb1(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); +} + +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_cb1, NULL, NULL); + if (!ret) + printf("bt_gatt_client_write_value() : Write FAILED"); + +} diff --git a/src/shared/micp.h b/src/shared/micp.h index 4a9807ed4..f20d46c9c 100644 --- a/src/shared/micp.h +++ b/src/shared/micp.h @@ -52,3 +52,5 @@ 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*/ + +void micp_write_value(struct bt_micp *micp, void *user_data); 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 +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include + + +#include + +#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..fe84fb02d --- /dev/null +++ b/unit/test-mics.c @@ -0,0 +1,290 @@ +// 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 +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include + + +#include + +#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_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_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(); +}