From patchwork Tue Feb 13 21:56:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ajay KV X-Patchwork-Id: 13555344 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7E2EA399 for ; Tue, 13 Feb 2024 16:30:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707841816; cv=none; b=dq2/8KHbnqK4p/OBfHTpHNWKWO303fHk8NpalkonBckVNeaF47vokl25jYwUWZo9jQf+fJ9qCrXVXPRQuUXhxU/5blXcboDh9f16NKVC6lVq3N5OiiF+M3z50/v0H0Bggoo1mpcH9cJGA6pymMHkfT6zHFSM39E08QX9rTfjOHk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707841816; c=relaxed/simple; bh=ZPhiTtmHA5Wz+bP/VwMzuP5GMayeXXPL5w4/3O/na9w=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=LBvuEOq+0h4Kwhmda9yHBjaaVk2f8rBvRTkJqfO/ElbyqukCwe2SCBc9U2hA2Ch+We3M/v+pVTrwyNO7gip/XwfAmiIn2MbUPQ5sKUNZ8JWz9ewUWF7dL5q/D06hZR1t/4om9b5GnrRpPw1HZvpklZki2qNqsPSblTVYTK0C4pU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=D55pZu3+; arc=none smtp.client-ip=192.198.163.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="D55pZu3+" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1707841814; x=1739377814; h=from:to:cc:subject:date:message-id:mime-version: content-transfer-encoding; bh=ZPhiTtmHA5Wz+bP/VwMzuP5GMayeXXPL5w4/3O/na9w=; b=D55pZu3+Fos+VMDPQkzKmi5bED3LImlMnbmncLqaE0vhR1utFz5DrRJu Sc2lHF79LjDdbs/vA2YrNj18lLWVTvr7PjlAbxUO8k6GVShj0iUx5Dyer kXdQLlwLI+fQsHEcvh6ftEhhBVSdL7Enm653Fk0bekpBsV1C1VCRaVu7T vWmdPDhnK1hjf0dtrv8x2muZlypEclWAiCQBlSRynyR6xEwkJa20EQGAq V/QElSu9oEXGupg26m4TT31c3QgF/yQf492cfFES9ZhcjUF4v/k9nrQGF Yuzlay65SZ3CYhyE3HrDy4vRPhIhklj1lnuK27ge3B5mAX8MR25Z+apZL A==; X-IronPort-AV: E=McAfee;i="6600,9927,10982"; a="5629500" X-IronPort-AV: E=Sophos;i="6.06,157,1705392000"; d="scan'208";a="5629500" Received: from orviesa009.jf.intel.com ([10.64.159.149]) by fmvoesa106.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Feb 2024 08:30:11 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.06,157,1705392000"; d="scan'208";a="2904747" Received: from weba0535.iind.intel.com ([10.224.186.30]) by orviesa009.jf.intel.com with ESMTP; 13 Feb 2024 08:30:10 -0800 From: Ajay KV To: linux-bluetooth@vger.kernel.org Cc: kiran.k@intel.com, Ajay KV Subject: [PATCH BlueZ 1/3] lib/uuid: Add support to compare 16bit uuids Date: Tue, 13 Feb 2024 23:56:58 +0200 Message-Id: <20240213215703.21507-1-ajay.k.v@intel.com> X-Mailer: git-send-email 2.34.1 Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 --- lib/uuid.c | 9 +++++++++ lib/uuid.h | 1 + 2 files changed, 10 insertions(+) diff --git a/lib/uuid.c b/lib/uuid.c index 1d2e1f7328b8..3ef45db5408c 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -120,6 +120,15 @@ int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2) return bt_uuid128_cmp(&u1, &u2); } +int bt_uuid16_cmp(const bt_uuid_t *uuid1, uint16_t uuid2) +{ + + if (!uuid1 || (uuid1->type != BT_UUID16)) + return 0; + + return (uuid1->value.u16 == uuid2); +} + /* * convert the UUID to string, copying a maximum of n characters. */ diff --git a/lib/uuid.h b/lib/uuid.h index 8839dea08140..124e74985a80 100644 --- a/lib/uuid.h +++ b/lib/uuid.h @@ -242,6 +242,7 @@ int bt_uuid32_create(bt_uuid_t *btuuid, uint32_t value); int bt_uuid128_create(bt_uuid_t *btuuid, uint128_t value); int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2); +int bt_uuid16_cmp(const bt_uuid_t *uuid1, uint16_t uuid2); void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst); #define MAX_LEN_UUID_STR 37 From patchwork Tue Feb 13 21:56:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ajay KV X-Patchwork-Id: 13555345 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 73DEEBA53 for ; Tue, 13 Feb 2024 16:30:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707841818; cv=none; b=K/UKkpfIKO+tRieWcuhkmuSVdcZ1yWpUBwE1nYyVZEXpPnbxv7LCv6Afg1ncOPc4abxB0dLPpJF8iRzajWbi1++1P5DWg7ddYOC08ZOF1RYhiRSplB7XHH2bCUc2y22nSprsXk/FHSDJ4jQmgwd74kzQxejgS8C+7ol6Mn0TTRA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707841818; c=relaxed/simple; bh=2JHVNISoH9PC5E92Apajn8Qk9GGQh+lYA3YlwsUOMfM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=PFEUTJNtE9Bt+R4Mn3ExDuWkAIbKeUBn2Uoi85eQTx7NLTEqLf2ZoAEyipGpJEX4n/zshTdKwBmpVyLKg4yDIN14HRJKcFWG8mWYXD6Qnb4D4+WFK7E/MqjuWUmMQOm1QVrt3g3Z8h/xN3udOR5s2BiA5DduM4BSfJPi2xVml7Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=nPx/9xeY; arc=none smtp.client-ip=192.198.163.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="nPx/9xeY" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1707841816; x=1739377816; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=2JHVNISoH9PC5E92Apajn8Qk9GGQh+lYA3YlwsUOMfM=; b=nPx/9xeYjSFkOHDPMpaa1/x+fBlLWMbz2pDsUrhu1k3/FKQ9fnvIFW1a Ir47acvBumhPSqm2ePiQzOURSkk3O7BdPRt8OWaeOVmiu9riIMpXIF92k HhiiCx8Qz9UHS2sOeqcrXfUtolaNVWYyPjwg7o4LUWC1F6Io0D1z2QyzL nmCq7BOmN129WmPAILXsIOV3+Q154KYg3Gug8Bdj3FaYz7+tC2zPuGHPn wto9pt7qDl/5UpasJ6jdzIpVttvGz9dvaZMBaHRFD35bgbdTr3UGOvXeg D+Un+tg2cikUbluzW0VjN51N3rBRE8VOR/j9wo0Ar4A3rP0AbILPBaUnL g==; X-IronPort-AV: E=McAfee;i="6600,9927,10982"; a="5629505" X-IronPort-AV: E=Sophos;i="6.06,157,1705392000"; d="scan'208";a="5629505" Received: from orviesa009.jf.intel.com ([10.64.159.149]) by fmvoesa106.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Feb 2024 08:30:12 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.06,157,1705392000"; d="scan'208";a="2904753" Received: from weba0535.iind.intel.com ([10.224.186.30]) by orviesa009.jf.intel.com with ESMTP; 13 Feb 2024 08:30:11 -0800 From: Ajay KV To: linux-bluetooth@vger.kernel.org Cc: kiran.k@intel.com, Ajay KV Subject: [PATCH BlueZ 2/3] lib/uuid: Add GTBS UUIDS for Call Control Profile. Date: Tue, 13 Feb 2024 23:56:59 +0200 Message-Id: <20240213215703.21507-2-ajay.k.v@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240213215703.21507-1-ajay.k.v@intel.com> References: <20240213215703.21507-1-ajay.k.v@intel.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 --- lib/uuid.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/uuid.h b/lib/uuid.h index 124e74985a80..8404b287ed01 100644 --- a/lib/uuid.h +++ b/lib/uuid.h @@ -221,6 +221,27 @@ extern "C" { #define MICS_UUID 0x184D #define MUTE_CHRC_UUID 0x2BC3 +/* Call Control Service(TBS/CCS) */ +#define TBS_UUID 0x184B +#define GTBS_UUID 0x184C + +#define BEARER_PROVIDER_NAME_CHRC_UUID 0x2bb3 +#define BEARER_UCI_CHRC_UUID 0x2bb4 +#define BEARER_TECH_CHRC_UUID 0x2bb5 +#define BEARER_URI_SCHEME_CHRC_UUID 0x2bb6 +#define BEARER_SIGNAL_STR_CHRC_UUID 0x2bb7 +#define BEARER_SIGNAL_INTRVL_CHRC_UUID 0x2bb8 +#define CURR_CALL_LIST_CHRC_UUID 0x2bb9 +#define BEARER_CCID_CHRC_UUID 0x2bba +#define CALL_STATUS_FLAG_CHRC_UUID 0x2bbb +#define INCOM_CALL_TARGET_URI_CHRC_UUID 0x2bbc +#define CALL_STATE_CHRC_UUID 0x2bbd +#define CALL_CTRL_POINT_CHRC_UUID 0x2bbe +#define CALL_CTRL_POINT_OPT_OPCODE_CHRC_UUID 0x2bbf +#define TERMINATION_REASON_CHRC_UUID 0x2bc0 +#define INCOMING_CALL_CHRC_UUID 0x2bc1 +#define CALL_FRIENDLY_NAME_CHRC_UUID 0x2bc2 + typedef struct { enum { BT_UUID_UNSPEC = 0, From patchwork Tue Feb 13 21:57:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ajay KV X-Patchwork-Id: 13555346 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DDCCEAD53 for ; Tue, 13 Feb 2024 16:30:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707841819; cv=none; b=rMWLvKJSNcmIbk1D5CpF5Eb+dwSpD3XDP4VL3JKdiZehGdarplYgu53y+HkbEvhw9Vpk/WGHvyK4bvs4yneT6D9QALhn0Y9eY4IMZilSPcvTA8mXJNxZGoqGfPNDPaMtZWjm8+5TlKX+X8EHY/6xHXEVqzb93vHa+sYZUojnns4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707841819; c=relaxed/simple; bh=mTUS3SLWvGyqyymXIMdJMOmF3VwB+FUVcYVGHpdHHeQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=F1oMWVFVw1EYTIJWfvW5nFkyVOgIRUelZTSGC6AQuXIY6+nb2YhECofOhabmFb0sUj2pAIhZb0BUKSXH5bcFUhm0CYH91EjQwjpmVImwizzHyTXhOTofOus6erhbaGRnOdeQYuq9BhDxVUjCGS6x85T8gbUYC5TjFsyZe1/6Gk4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=FOUVAXiR; arc=none smtp.client-ip=192.198.163.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="FOUVAXiR" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1707841817; x=1739377817; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=mTUS3SLWvGyqyymXIMdJMOmF3VwB+FUVcYVGHpdHHeQ=; b=FOUVAXiRbu7CfJ1Qt+d6o0s6fL/DLcqBykyfYCg/MQWBiKlf1BWjUtge zHY8fcogJVQNHWGOvrQenfAjqiMYY7eWmpt1sO05Oxn1EWPbH1wR8Po65 v+pMDOJQnBzAjkd2/+88vfyAxtzF7XTnf1gpUgttGUpcO52plgN9Wp4Hp Kb36EuEUVFVksz6JWNxFM/46AyePGPTNI0b1BhIxHRWlOahicCbWAfNFm FeaF7RkhMJAsTOrM7tvJ6r9uyoKcKqdzGzPmdfJf7CIWIOJTaDIUNTzDc c5GBmDJjwSg9NC6YL9SmxM6fOu9AictZ2K7Aoga1n0Gb50YhpHncTIHwy g==; X-IronPort-AV: E=McAfee;i="6600,9927,10982"; a="5629506" X-IronPort-AV: E=Sophos;i="6.06,157,1705392000"; d="scan'208";a="5629506" Received: from orviesa009.jf.intel.com ([10.64.159.149]) by fmvoesa106.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Feb 2024 08:30:14 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.06,157,1705392000"; d="scan'208";a="2904765" Received: from weba0535.iind.intel.com ([10.224.186.30]) by orviesa009.jf.intel.com with ESMTP; 13 Feb 2024 08:30:13 -0800 From: Ajay KV To: linux-bluetooth@vger.kernel.org Cc: kiran.k@intel.com, Ajay KV Subject: [PATCH BlueZ 3/3] shared/ccp: Add initial code for Call Control Profile for Client Role. Date: Tue, 13 Feb 2024 23:57:00 +0200 Message-Id: <20240213215703.21507-3-ajay.k.v@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240213215703.21507-1-ajay.k.v@intel.com> References: <20240213215703.21507-1-ajay.k.v@intel.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 --- Makefile.am | 1 + src/shared/ccp.c | 1147 ++++++++++++++++++++++++++++++++++++++++++++++ src/shared/ccp.h | 45 ++ 3 files changed, 1193 insertions(+) create mode 100644 src/shared/ccp.c create mode 100644 src/shared/ccp.h diff --git a/Makefile.am b/Makefile.am index 2b1b9acdf825..fdffdc478a16 100644 --- a/Makefile.am +++ b/Makefile.am @@ -230,6 +230,7 @@ shared_sources = src/shared/io.h src/shared/timeout.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/ccp.h src/shared/ccp.c \ src/shared/lc3.h src/shared/tty.h if READLINE diff --git a/src/shared/ccp.c b/src/shared/ccp.c new file mode 100644 index 000000000000..9089779603bf --- /dev/null +++ b/src/shared/ccp.c @@ -0,0 +1,1147 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2022 Intel Corporation. All rights reserved. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "lib/bluetooth.h" +#include "lib/uuid.h" +#include "lib/hci.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-client.h" +#include "src/shared/ccp.h" + +#define DBG(_ccp, fmt, arg...) \ + ccp_debug(_ccp, "%s:%s() " fmt, __FILE__, __func__, ## arg) + +struct bt_ccp_db { + struct gatt_db *db; + struct bt_ccs *ccs; +}; + +struct bt_ccp_pending { + unsigned int id; + struct bt_ccp *ccp; + bt_gatt_client_read_callback_t func; + void *user_data; +}; + +struct event_callback { + const struct bt_ccp_event_callback *cbs; + void *user_data; +}; + +struct bt_ccp { + int ref_count; + struct bt_gatt_client *client; + struct bt_ccp_db *ldb; + struct bt_ccp_db *rdb; + + unsigned int bearer_name_id; + unsigned int bearer_uci_id; + unsigned int bearer_technology_id; + unsigned int bearer_uri_schemes_list_id; + unsigned int signal_strength_id; + unsigned int signal_reporting_intrvl_id; + unsigned int current_call_list_id; + unsigned int ccid_id; + unsigned int status_flag_id; + unsigned int target_bearer_uri_id; + unsigned int call_state_id; + unsigned int call_control_pt_id; + unsigned int call_control_opt_opcode_id; + unsigned int termination_reason_id; + unsigned int incoming_call_id; + unsigned int friendly_name_id; + + struct event_callback *cb; + struct queue *pending; + + bt_ccp_debug_func_t debug_func; + bt_ccp_destroy_func_t debug_destroy; + void *debug_data; + void *user_data; +}; + +struct bt_ccs { + struct bt_ccp_db *mdb; + struct gatt_db_attribute *service; + struct gatt_db_attribute *bearer_name; + struct gatt_db_attribute *bearer_name_ccc; + struct gatt_db_attribute *bearer_uci; + struct gatt_db_attribute *bearer_technology; + struct gatt_db_attribute *bearer_technology_ccc; + struct gatt_db_attribute *bearer_uri_schemes_list; + struct gatt_db_attribute *signal_strength; + struct gatt_db_attribute *signal_strength_ccc; + struct gatt_db_attribute *signal_reporting_intrvl; + struct gatt_db_attribute *current_call_list; + struct gatt_db_attribute *current_call_list_ccc; + struct gatt_db_attribute *ccid; + struct gatt_db_attribute *status_flag; + struct gatt_db_attribute *status_flag_ccc; + struct gatt_db_attribute *target_bearer_uri; + struct gatt_db_attribute *call_state; + struct gatt_db_attribute *call_state_ccc; + struct gatt_db_attribute *call_ctrl_point; + struct gatt_db_attribute *call_ctrl_point_ccc; + struct gatt_db_attribute *call_ctrl_opt_opcode; + struct gatt_db_attribute *termination_reason; + struct gatt_db_attribute *termination_reason_ccc; + struct gatt_db_attribute *incoming_call; + struct gatt_db_attribute *incoming_call_ccc; + struct gatt_db_attribute *friendly_name; + struct gatt_db_attribute *friendly_name_ccc; +}; + +static struct queue *ccp_db; + +static void ccp_debug(struct bt_ccp *ccp, const char *format, ...) +{ + va_list ap; + + if (!ccp || !format || !ccp->debug_func) + return; + + va_start(ap, format); + util_debug_va(ccp->debug_func, ccp->debug_data, format, ap); + va_end(ap); +} + +static bool ccp_db_match(const void *data, const void *match_data) +{ + const struct bt_ccp_db *mdb = data; + const struct gatt_db *db = match_data; + + return (mdb->db == db); +} + +static void ccp_db_free(void *data) +{ + struct bt_ccp_db *bdb = data; + + if (!bdb) + return; + + gatt_db_unref(bdb->db); + + free(bdb->ccs); + free(bdb); +} + +static void ccp_free(void *data) +{ + struct bt_ccp *ccp = data; + + DBG(ccp, ""); + + bt_ccp_detach(ccp); + ccp_db_free(ccp->rdb); + queue_destroy(ccp->pending, NULL); + + free(ccp); +} + +struct bt_ccp *bt_ccp_ref(struct bt_ccp *ccp) +{ + if (!ccp) + return NULL; + + __sync_fetch_and_add(&ccp->ref_count, 1); + + return ccp; +} + +void bt_ccp_unref(struct bt_ccp *ccp) +{ + if (!ccp) + return; + + if (__sync_sub_and_fetch(&ccp->ref_count, 1)) + return; + + ccp_free(ccp); +} + +bool bt_ccp_set_user_data(struct bt_ccp *ccp, void *user_data) +{ + if (!ccp) + return false; + + ccp->user_data = user_data; + + return true; +} + +void *bt_ccp_get_user_data(struct bt_ccp *ccp) +{ + if (!ccp) + return NULL; + + return ccp->user_data; +} + +bool bt_ccp_set_debug(struct bt_ccp *ccp, bt_ccp_debug_func_t func, + void *user_data, bt_ccp_destroy_func_t destroy) +{ + if (!ccp) + return false; + + if (ccp->debug_destroy) + ccp->debug_destroy(ccp->debug_data); + + ccp->debug_func = func; + ccp->debug_destroy = destroy; + ccp->debug_data = user_data; + + return true; +} + +static void ccs_call_state_read(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + int call_state = 0; + struct iovec iov; + + iov.iov_base = &call_state; + iov.iov_len = sizeof(int); + + gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, + iov.iov_len); +} + +static void ccs_call_state_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) +{ + gatt_db_attribute_write_result(attrib, id, + BT_ATT_ERROR_INSUFFICIENT_RESOURCES); +} + +static struct bt_ccs *ccs_new(struct gatt_db *db) +{ + struct bt_ccs *ccs; + bt_uuid_t uuid; + + if (!db) + return NULL; + + ccs = new0(struct bt_ccs, 1); + + /* Populate DB with ccs attributes */ + bt_uuid16_create(&uuid, GTBS_UUID); + ccs->service = gatt_db_add_service(db, &uuid, true, 42); + + bt_uuid16_create(&uuid, BEARER_PROVIDER_NAME_CHRC_UUID); + ccs->bearer_name = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, + ccs_call_state_read, NULL, ccs); + + ccs->bearer_name_ccc = gatt_db_service_add_ccc(ccs->service, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); + + bt_uuid16_create(&uuid, BEARER_UCI_CHRC_UUID); + ccs->bearer_uci = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ, ccs_call_state_read, + NULL, ccs); + + bt_uuid16_create(&uuid, BEARER_TECH_CHRC_UUID); + ccs->bearer_technology = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, + ccs_call_state_read, NULL, + ccs); + + ccs->bearer_technology_ccc = gatt_db_service_add_ccc(ccs->service, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); + + bt_uuid16_create(&uuid, BEARER_URI_SCHEME_CHRC_UUID); + ccs->bearer_uri_schemes_list = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ, + ccs_call_state_read, NULL, + ccs); + + bt_uuid16_create(&uuid, BEARER_SIGNAL_STR_CHRC_UUID); + ccs->signal_strength = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, + ccs_call_state_read, NULL, + ccs); + + ccs->signal_strength_ccc = gatt_db_service_add_ccc(ccs->service, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); + + bt_uuid16_create(&uuid, BEARER_SIGNAL_INTRVL_CHRC_UUID); + ccs->signal_reporting_intrvl = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, + BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE | + BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP, + ccs_call_state_read, ccs_call_state_write, + ccs); + + bt_uuid16_create(&uuid, CURR_CALL_LIST_CHRC_UUID); + ccs->current_call_list = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, + ccs_call_state_read, NULL, + ccs); + + ccs->current_call_list_ccc = gatt_db_service_add_ccc(ccs->service, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); + + bt_uuid16_create(&uuid, BEARER_CCID_CHRC_UUID); + ccs->ccid = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ, + ccs_call_state_read, NULL, + ccs); + + bt_uuid16_create(&uuid, CALL_STATUS_FLAG_CHRC_UUID); + ccs->status_flag = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, + ccs_call_state_read, NULL, + ccs); + + ccs->status_flag_ccc = gatt_db_service_add_ccc(ccs->service, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); + + bt_uuid16_create(&uuid, INCOM_CALL_TARGET_URI_CHRC_UUID); + ccs->target_bearer_uri = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ, + ccs_call_state_read, NULL, + ccs); + + bt_uuid16_create(&uuid, CALL_STATE_CHRC_UUID); + ccs->call_ctrl_point = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, + ccs_call_state_read, NULL, + ccs); + + ccs->call_ctrl_point_ccc = gatt_db_service_add_ccc(ccs->service, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); + + bt_uuid16_create(&uuid, CALL_CTRL_POINT_CHRC_UUID); + ccs->call_ctrl_opt_opcode = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_WRITE, + BT_GATT_CHRC_PROP_WRITE | BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP | + BT_GATT_CHRC_PROP_NOTIFY, + NULL, ccs_call_state_write, + ccs); + + bt_uuid16_create(&uuid, CALL_CTRL_POINT_OPT_OPCODE_CHRC_UUID); + ccs->call_ctrl_opt_opcode = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ, + ccs_call_state_read, NULL, + ccs); + + bt_uuid16_create(&uuid, TERMINATION_REASON_CHRC_UUID); + ccs->termination_reason = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, + ccs_call_state_read, NULL, + ccs); + + bt_uuid16_create(&uuid, INCOMING_CALL_CHRC_UUID); + ccs->incoming_call = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_NONE, + BT_GATT_CHRC_PROP_NOTIFY, + NULL, NULL, + ccs); + + ccs->incoming_call_ccc = gatt_db_service_add_ccc(ccs->service, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); + + bt_uuid16_create(&uuid, CALL_FRIENDLY_NAME_CHRC_UUID); + ccs->friendly_name = gatt_db_service_add_characteristic(ccs->service, + &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY, + ccs_call_state_read, NULL, + ccs); + + ccs->friendly_name_ccc = gatt_db_service_add_ccc(ccs->service, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); + + gatt_db_service_set_active(ccs->service, false); + + return ccs; +} + +static struct bt_ccs *ccp_get_ccs(struct bt_ccp *ccp) +{ + if (!ccp) + return NULL; + + if (ccp->rdb->ccs) + return ccp->rdb->ccs; + + ccp->rdb->ccs = new0(struct bt_ccs, 1); + ccp->rdb->ccs->mdb = ccp->rdb; + + return ccp->rdb->ccs; +} + +static void ccp_pending_destroy(void *data) +{ + struct bt_ccp_pending *pending = data; + struct bt_ccp *ccp = pending->ccp; + + queue_remove_if(ccp->pending, NULL, pending); +} + +static void ccp_pending_complete(bool success, uint8_t att_ecode, + const uint8_t *value, uint16_t length, + void *user_data) +{ + struct bt_ccp_pending *pending = user_data; + + if (pending->func) + pending->func(success, att_ecode, value, length, + pending->user_data); +} + +static void ccp_read_value(struct bt_ccp *ccp, uint16_t value_handle, + bt_gatt_client_read_callback_t func, + void *user_data) +{ + struct bt_ccp_pending *pending; + + pending = new0(struct bt_ccp_pending, 1); + pending->ccp = ccp; + pending->func = func; + pending->user_data = user_data; + + pending->id = bt_gatt_client_read_value(ccp->client, value_handle, + ccp_pending_complete, pending, + ccp_pending_destroy); + if (!pending->id) { + DBG(ccp, "Unable to send Read request"); + free(pending); + return; + } + + queue_push_tail(ccp->pending, pending); +} + +static void read_call_back(bool success, uint8_t att_ecode, + const uint8_t *value, uint16_t length, + void *user_data) +{ + struct bt_ccp *ccp = user_data; + + DBG(ccp, ""); + + if (!success) { + DBG(ccp, "Unable to read call state: error 0x%02x", + att_ecode); + return; + } +} + +static void ccp_cb_register(uint16_t att_ecode, void *user_data) +{ + struct bt_ccp *ccp = user_data; + + if (att_ecode) + DBG(ccp, "ccp cb notification failed: 0x%04x", + att_ecode); + + /* TODO: generic handler for non-mandatory CCP call backs */ +} + +static void ccp_cb_notify(uint16_t value_handle, const uint8_t *value, + uint16_t length, void *user_data) +{ + /* TODO: generic handler for non-mandatory CCP notifications */ +} + +static void ccp_cb_status_flag_register(uint16_t att_ecode, void *user_data) +{ + struct bt_ccp *ccp = user_data; + + if (att_ecode) + DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); +} + +static void ccp_cb_status_flag_notify(uint16_t value_handle, const uint8_t *value, + uint16_t length, void *user_data) +{ + struct bt_ccp *ccp = user_data; + + DBG(ccp, ""); + + if (!length) + return; +} + +static void ccp_cb_terminate_register(uint16_t att_ecode, void *user_data) +{ + struct bt_ccp *ccp = user_data; + + if (att_ecode) + DBG(ccp, "ccp cb notification failed: 0x%04x", + att_ecode); +} + +static void ccp_cb_terminate_notify(uint16_t value_handle, const uint8_t *value, + uint16_t length, void *user_data) +{ + struct bt_ccp *ccp = user_data; + + DBG(ccp, ""); + + if (!length) + return; + + /* TODO: update call state in Local context */ +} + +static void ccp_cb_bearer_name_register(uint16_t att_ecode, void *user_data) +{ + struct bt_ccp *ccp = user_data; + + DBG(ccp, ""); + + if (att_ecode) + DBG(ccp, "ccp cb notification failed: 0x%04x", + att_ecode); +} + +static void ccp_cb_bearer_name_notify(uint16_t value_handle, const uint8_t *value, + uint16_t length, void *user_data) +{ + struct bt_ccp *ccp = user_data; + + DBG(ccp, ""); + + if (!length) + return; + + /* TODO: update call details in Local context */ +} + +static void ccp_cb_call_list_register(uint16_t att_ecode, void *user_data) +{ + struct bt_ccp *ccp = user_data; + + DBG(ccp, ""); + + if (att_ecode) + DBG(ccp, "ccp cb notification failed: 0x%04x", att_ecode); +} + +static void ccp_cb_call_list_notify(uint16_t value_handle, const uint8_t *value, + uint16_t length, void *user_data) +{ + struct bt_ccp *ccp = user_data; + + DBG(ccp, ""); + + if (!length) + return; + + /* TODO: update call list in Local context */ +} + +static void ccp_cb_call_state_register(uint16_t att_ecode, void *user_data) +{ + struct bt_ccp *ccp = user_data; + + DBG(ccp, ""); + + if (att_ecode) + DBG(ccp, "ccp cb notification failed: 0x%04x", + att_ecode); +} + +static void ccp_cb_call_state_notify(uint16_t value_handle, const uint8_t *value, + uint16_t length, void *user_data) +{ + struct bt_ccp *ccp = user_data; + + DBG(ccp, ""); + + if (!length) + return; + + /* TODO: update call state in Local context */ +} + +static void ccp_cb_incom_call_register(uint16_t att_ecode, void *user_data) +{ + struct bt_ccp *ccp = user_data; + + DBG(ccp, ""); + + if (att_ecode) + DBG(ccp, "ccp cb notification failed: 0x%04x", + att_ecode); +} + +static void ccp_cb_incom_call_notify(uint16_t value_handle, const uint8_t *value, + uint16_t length, void *user_data) +{ + struct bt_ccp *ccp = user_data; + + DBG(ccp, ""); + + if (!length) + return; + + /* TODO: Handle incoming call notofiation, Answer/reject etc */ +} + +static void bt_ccp_incom_call_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->incoming_call, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->incoming_call_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_incom_call_register, + ccp_cb_incom_call_notify, ccp, NULL); +} + +static void bt_ccp_call_state_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->call_state, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->call_state_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_call_state_register, + ccp_cb_call_state_notify, ccp, NULL); +} + +static void bt_ccp_call_list_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->current_call_list, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->current_call_list_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_call_list_register, + ccp_cb_call_list_notify, ccp, NULL); +} + +static void bt_ccp_name_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->bearer_name, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->bearer_name_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_bearer_name_register, + ccp_cb_bearer_name_notify, ccp, NULL); +} + +static void bt_ccp_term_reason_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->termination_reason, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->termination_reason_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_terminate_register, + ccp_cb_terminate_notify, ccp, NULL); +} + +static void bt_ccp_status_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->status_flag, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->status_flag_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_status_flag_register, + ccp_cb_status_flag_notify, ccp, NULL); +} + +static void bt_ccp_uci_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->bearer_uci, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->bearer_uci_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_register, + ccp_cb_notify, ccp, NULL); +} + +static void bt_ccp_technology_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->bearer_technology, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->bearer_technology_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_register, + ccp_cb_notify, ccp, NULL); +} +static void bt_ccp_strength_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->signal_strength, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->signal_strength_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_register, + ccp_cb_notify, ccp, NULL); +} + +static void bt_ccp_ccid_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->ccid, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->ccid_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_register, + ccp_cb_notify, ccp, NULL); +} +static void bt_ccp_tar_uri_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->target_bearer_uri, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->target_bearer_uri_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_register, + ccp_cb_notify, ccp, NULL); +} + +static void bt_ccp_ctrl_point_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->call_ctrl_point, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->call_control_pt_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_register, + ccp_cb_notify, ccp, NULL); +} + +static void bt_ccp_ctrl_opcode_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->call_ctrl_opt_opcode, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->call_control_opt_opcode_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_register, + ccp_cb_notify, ccp, NULL); +} + +static void bt_ccp_friendly_name_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->friendly_name, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->friendly_name_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_register, + ccp_cb_notify, ccp, NULL); +} + +static void bt_ccp_signal_intrvl_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->signal_reporting_intrvl, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->signal_reporting_intrvl_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_register, + ccp_cb_notify, ccp, NULL); +} + +static void bt_ccp_uri_list_attach(struct bt_ccp *ccp) +{ + uint16_t value_handle; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + DBG(ccp, ""); + + if (!gatt_db_attribute_get_char_data(ccs->bearer_uri_schemes_list, NULL, &value_handle, + NULL, NULL, NULL)) + return; + + ccp_read_value(ccp, value_handle, read_call_back, ccp); + + ccp->bearer_uri_schemes_list_id = bt_gatt_client_register_notify(ccp->client, + value_handle, ccp_cb_register, + ccp_cb_notify, ccp, NULL); +} + +static void foreach_ccs_char(struct gatt_db_attribute *attr, void *user_data) +{ + struct bt_ccp *ccp = user_data; + struct bt_ccs *ccs; + uint16_t value_handle; + bt_uuid_t uuid; + + if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, + NULL, NULL, &uuid)) + return; + + ccs = ccp_get_ccs(ccp); + if (!ccs || ccs->call_state) + return; + + if (bt_uuid16_cmp(&uuid, BEARER_PROVIDER_NAME_CHRC_UUID)) { + DBG(ccp, "Found Bearer Name, handle 0x%04x", value_handle); + + ccs->bearer_name = attr; + bt_ccp_name_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, BEARER_UCI_CHRC_UUID)) { + DBG(ccp, "Found Bearer Uci, handle 0x%04x", value_handle); + + ccs->bearer_uci = attr; + bt_ccp_uci_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, BEARER_TECH_CHRC_UUID)) { + DBG(ccp, "Found Bearer Technology, handle 0x%04x", value_handle); + + ccs->bearer_technology = attr; + bt_ccp_technology_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, BEARER_SIGNAL_STR_CHRC_UUID)) { + DBG(ccp, "Found Signal Strength, handle 0x%04x", value_handle); + + ccs->signal_strength = attr; + bt_ccp_strength_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, BEARER_SIGNAL_INTRVL_CHRC_UUID)) { + DBG(ccp, "Found Signal Interval, handle 0x%04x", value_handle); + + ccs->signal_reporting_intrvl = attr; + bt_ccp_signal_intrvl_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, CALL_STATUS_FLAG_CHRC_UUID)) { + DBG(ccp, "Found Status Flag, handle 0x%04x", value_handle); + + ccs->status_flag = attr; + bt_ccp_status_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, BEARER_URI_SCHEME_CHRC_UUID)) { + DBG(ccp, "Found URI Scheme, handle 0x%04x", value_handle); + + ccs->bearer_uri_schemes_list = attr; + bt_ccp_uri_list_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, CURR_CALL_LIST_CHRC_UUID)) { + DBG(ccp, "Found Call List, handle 0x%04x", value_handle); + + ccs->current_call_list = attr; + bt_ccp_call_list_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, BEARER_CCID_CHRC_UUID)) { + DBG(ccp, "Found CCID, handle 0x%04x", value_handle); + + ccs->ccid = attr; + bt_ccp_ccid_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, INCOM_CALL_TARGET_URI_CHRC_UUID)) { + DBG(ccp, "Found Bearer Uri, handle 0x%04x", value_handle); + + ccs->target_bearer_uri = attr; + bt_ccp_tar_uri_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, CALL_CTRL_POINT_CHRC_UUID)) { + DBG(ccp, "Found Control Point, handle 0x%04x", value_handle); + + ccs->call_ctrl_point = attr; + bt_ccp_ctrl_point_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, CALL_CTRL_POINT_OPT_OPCODE_CHRC_UUID)) { + DBG(ccp, "Found Control opcode, handle 0x%04x", value_handle); + + ccs->call_ctrl_opt_opcode = attr; + bt_ccp_ctrl_opcode_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, TERMINATION_REASON_CHRC_UUID)) { + DBG(ccp, "Found Termination Reason, handle 0x%04x", value_handle); + + ccs->termination_reason = attr; + bt_ccp_term_reason_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, INCOMING_CALL_CHRC_UUID)) { + DBG(ccp, "Found Incoming call, handle 0x%04x", value_handle); + + ccs->incoming_call = attr; + bt_ccp_incom_call_attach(ccp); + } + + if (bt_uuid16_cmp(&uuid, CALL_FRIENDLY_NAME_CHRC_UUID)) { + DBG(ccp, "Found Friendly name, handle 0x%04x", value_handle); + + ccs->friendly_name = attr; + bt_ccp_friendly_name_attach(ccp); + } +} + +void bt_ccp_set_event_callbacks(struct bt_ccp *ccp, + const struct bt_ccp_event_callback *cbs, + void *user_data) +{ + struct event_callback *cb; + + if (ccp->cb) + free(ccp->cb); + + cb = new0(struct event_callback, 1); + cb->cbs = cbs; + cb->user_data = user_data; + + ccp->cb = cb; +} + +static void foreach_ccs_service(struct gatt_db_attribute *attr, + void *user_data) +{ + struct bt_ccp *ccp = user_data; + struct bt_ccs *ccs = ccp_get_ccs(ccp); + + ccs->service = attr; + + gatt_db_service_foreach_char(attr, foreach_ccs_char, ccp); +} + +static struct bt_ccp_db *ccp_db_new(struct gatt_db *db) +{ + struct bt_ccp_db *mdb; + + if (!db) + return NULL; + + mdb = new0(struct bt_ccp_db, 1); + mdb->db = gatt_db_ref(db); + + if (!ccp_db) + ccp_db = queue_new(); + + queue_push_tail(ccp_db, mdb); + + mdb->ccs = ccs_new(db); + return mdb; +} + +static struct bt_ccp_db *ccp_get_db(struct gatt_db *db) +{ + struct bt_ccp_db *mdb; + + mdb = queue_find(ccp_db, ccp_db_match, db); + if (mdb) + return mdb; + + return ccp_db_new(db); +} + +struct bt_ccp *bt_ccp_new(struct gatt_db *ldb, struct gatt_db *rdb) +{ + struct bt_ccp *ccp; + struct bt_ccp_db *mdb; + + if (!ldb) + return NULL; + + mdb = ccp_get_db(ldb); + if (!mdb) + return NULL; + + ccp = new0(struct bt_ccp, 1); + ccp->ldb = mdb; + ccp->pending = queue_new(); + + if (!rdb) + goto done; + + mdb = new0(struct bt_ccp_db, 1); + mdb->db = gatt_db_ref(rdb); + + ccp->rdb = mdb; + +done: + bt_ccp_ref(ccp); + + return ccp; +} + +void bt_ccp_register(struct gatt_db *db) +{ + ccp_db_new(db); +} + +bool bt_ccp_attach(struct bt_ccp *ccp, struct bt_gatt_client *client) +{ + bt_uuid_t uuid; + + DBG(ccp, "ccp %p", ccp); + + ccp->client = bt_gatt_client_clone(client); + if (!ccp->client) + return false; + + if (ccp->rdb->ccs) { + bt_ccp_call_state_attach(ccp); + return true; + } + + bt_uuid16_create(&uuid, GTBS_UUID); + gatt_db_foreach_service(ccp->rdb->db, &uuid, foreach_ccs_service, ccp); + + return true; +} + +void bt_ccp_detach(struct bt_ccp *ccp) +{ + DBG(ccp, "%p", ccp); + + bt_gatt_client_unref(ccp->client); + ccp->client = NULL; +} diff --git a/src/shared/ccp.h b/src/shared/ccp.h new file mode 100644 index 000000000000..809986c2601a --- /dev/null +++ b/src/shared/ccp.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2020 Intel Corporation. All rights reserved. + * + */ + +#include +#include + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +struct bt_ccp; +struct bt_ccp_db; +struct bt_ccp_session_info; + +typedef void (*bt_ccp_debug_func_t)(const char *str, void *user_data); +typedef void (*bt_ccp_destroy_func_t)(void *user_data); + +struct bt_ccp_event_callback { + void (*call_state)(struct bt_ccp *ccp, const uint8_t *value, + uint16_t length); +}; + +void bt_ccp_set_event_callbacks(struct bt_ccp *ccp, + const struct bt_ccp_event_callback *cbs, + void *user_data); + +bool bt_ccp_set_debug(struct bt_ccp *ccp, bt_ccp_debug_func_t cb, + void *user_data, bt_ccp_destroy_func_t destroy); + +void bt_ccp_register(struct gatt_db *db); +bool bt_ccp_attach(struct bt_ccp *ccp, struct bt_gatt_client *client); +void bt_ccp_detach(struct bt_ccp *ccp); + +struct bt_ccp *bt_ccp_new(struct gatt_db *ldb, struct gatt_db *rdb); +struct bt_ccp *bt_ccp_ref(struct bt_ccp *ccp); +void bt_ccp_unref(struct bt_ccp *ccp); + +bool bt_ccp_set_user_data(struct bt_ccp *ccp, void *user_data); +void *bt_ccp_get_user_data(struct bt_ccp *ccp);