From patchwork Thu Jan 6 21:03:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 530399 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 E9D26C4332F for ; Thu, 6 Jan 2022 21:03:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244085AbiAFVDX (ORCPT ); Thu, 6 Jan 2022 16:03:23 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34222 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S244079AbiAFVDX (ORCPT ); Thu, 6 Jan 2022 16:03:23 -0500 Received: from mail-pg1-x52d.google.com (mail-pg1-x52d.google.com [IPv6:2607:f8b0:4864:20::52d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D8E5FC061245 for ; Thu, 6 Jan 2022 13:03:22 -0800 (PST) Received: by mail-pg1-x52d.google.com with SMTP id g22so3663020pgn.1 for ; Thu, 06 Jan 2022 13:03:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=fTLAyC+e1QiFsRyR20ss1eZCd7NrvxekqXf9+xLucYg=; b=bPHJF80tZ3t/zfN5WruPGtJK+DrLpYrPhZUmj1ymnh4HOY3JevtFHz+YYBr+6mlfr5 mF+J8GMxROAEmvfghWqX4W0QG/EbeXqn9bASxm/7bQ/7bRSapT+41tjFycvJcZX0bl9S A8nVzF8sM6W1dcyylDZqxaneQF4Xd+7k/FztaHsnGd26D6MLBW+DuphWdes8/Dgr4ZS8 pqjsyboEPxcNLWrAKDGg/YU1V23iWL6+4PF/Jt2uc5OqMaQxZDwwRBPP1+H2k6GCuzjM rCFIGnHOI0WW0ITban2fGYjoBW7glgzTucuh9TbMIcdi615BXZKImVj0UH5OfKcotFF1 2OmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=fTLAyC+e1QiFsRyR20ss1eZCd7NrvxekqXf9+xLucYg=; b=MGuMA6k/pM4OIX9yKsQqXpG2sskIMlENwHCdhN5rCu/UwlXQnXBx2DjGLfedOE8g91 /QOPHY+A5/Fldliew9xB2GkAx8cMMUrJLaa2ZwdlveYByGCu0IT89CJiIFJDTdGEVIGC R9nxiGEMsNefzJhOLnYUBIWoUZ19K36DjH/gUQUFg1uRMiu396KMLuKkIzZ7v/B/XMN6 11F/C71crwGJn/Fgxp9nIHXXWerYlgNFEcRLHXnyRqFpBqNnTW4D9skigt7YwbdgyV1/ +4izqELD77s89KMOkYLMWz5RGwZgZmuafMz6bd/qlA+Mf+Wl0PpqF0ERSHTN1qGwpph0 USfA== X-Gm-Message-State: AOAM533EVI2+qi2woIfye6DcpGRZ/sgrOlHuCh/gmzIRtCcSExGVPjjq GcFV+kcRPSs23yh4ZjyIq9P/O5filV8= X-Google-Smtp-Source: ABdhPJy5WleA/8h+KETrp5Frzcp5BIwYZrH+121PiFyGjYbesrit9L2Xnds9G+ViBig33Y9wS29tCw== X-Received: by 2002:a65:680a:: with SMTP id l10mr53356481pgt.52.1641503001281; Thu, 06 Jan 2022 13:03:21 -0800 (PST) Received: from lvondent-mobl4.. (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id s25sm3091853pfg.208.2022.01.06.13.03.20 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Jan 2022 13:03:20 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ 2/4] attrib: Remove gatt-service.{c,h} and attrib-server.{c,h} Date: Thu, 6 Jan 2022 13:03:17 -0800 Message-Id: <20220106210319.3620823-2-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20220106210319.3620823-1-luiz.dentz@gmail.com> References: <20220106210319.3620823-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz These files are not longer needed since this functionality has been moved to gatt-db.{c,h}. --- Makefile.am | 4 +- attrib/gatt-service.c | 362 --------- attrib/gatt-service.h | 44 -- src/adapter.c | 5 - src/attrib-server.c | 1656 ----------------------------------------- src/attrib-server.h | 29 - src/device.c | 1 - 7 files changed, 1 insertion(+), 2100 deletions(-) delete mode 100644 attrib/gatt-service.c delete mode 100644 attrib/gatt-service.h delete mode 100644 src/attrib-server.c delete mode 100644 src/attrib-server.h diff --git a/Makefile.am b/Makefile.am index 308f13c50..a26ba9bc7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -267,8 +267,7 @@ endif attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \ attrib/gatt.h attrib/gatt.c \ - attrib/gattrib.h attrib/gattrib.c \ - attrib/gatt-service.h attrib/gatt-service.c + attrib/gattrib.h attrib/gattrib.c btio_sources = btio/btio.h btio/btio.c @@ -305,7 +304,6 @@ src_bluetoothd_SOURCES = $(builtin_sources) \ src/rfkill.c src/btd.h src/sdpd.h \ src/sdpd-server.c src/sdpd-request.c \ src/sdpd-service.c src/sdpd-database.c \ - src/attrib-server.h src/attrib-server.c \ src/gatt-database.h src/gatt-database.c \ src/sdp-xml.h src/sdp-xml.c \ src/sdp-client.h src/sdp-client.c \ diff --git a/attrib/gatt-service.c b/attrib/gatt-service.c deleted file mode 100644 index 741ff1acb..000000000 --- a/attrib/gatt-service.c +++ /dev/null @@ -1,362 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2011 Nokia Corporation - * Copyright (C) 2011 Marcel Holtmann - * - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include "lib/bluetooth.h" -#include "lib/sdp.h" -#include "lib/uuid.h" - -#include "src/adapter.h" -#include "src/shared/util.h" -#include "attrib/gattrib.h" -#include "attrib/att.h" -#include "attrib/gatt.h" -#include "attrib/att-database.h" -#include "src/attrib-server.h" -#include "attrib/gatt-service.h" -#include "src/log.h" - -struct gatt_info { - bt_uuid_t uuid; - uint8_t props; - int authentication; - int authorization; - GSList *callbacks; - unsigned int num_attrs; - uint16_t *value_handle; - uint16_t *ccc_handle; -}; - -struct attrib_cb { - attrib_event_t event; - void *fn; - void *user_data; -}; - -static inline void put_uuid_le(const bt_uuid_t *src, void *dst) -{ - if (src->type == BT_UUID16) - put_le16(src->value.u16, dst); - else - /* Convert from 128-bit BE to LE */ - bswap_128(&src->value.u128, dst); -} - -static GSList *parse_opts(gatt_option opt1, va_list args) -{ - gatt_option opt = opt1; - struct gatt_info *info; - struct attrib_cb *cb; - GSList *l = NULL; - - info = g_new0(struct gatt_info, 1); - l = g_slist_append(l, info); - - while (opt != GATT_OPT_INVALID) { - switch (opt) { - case GATT_OPT_CHR_UUID16: - bt_uuid16_create(&info->uuid, va_arg(args, int)); - /* characteristic declaration and value */ - info->num_attrs += 2; - break; - case GATT_OPT_CHR_UUID: - memcpy(&info->uuid, va_arg(args, bt_uuid_t *), - sizeof(bt_uuid_t)); - /* characteristic declaration and value */ - info->num_attrs += 2; - break; - case GATT_OPT_CHR_PROPS: - info->props = va_arg(args, int); - - if (info->props & (GATT_CHR_PROP_NOTIFY | - GATT_CHR_PROP_INDICATE)) - /* client characteristic configuration */ - info->num_attrs += 1; - - /* TODO: "Extended Properties" property requires a - * descriptor, but it is not supported yet. */ - break; - case GATT_OPT_CHR_VALUE_CB: - cb = g_new0(struct attrib_cb, 1); - cb->event = va_arg(args, attrib_event_t); - cb->fn = va_arg(args, void *); - cb->user_data = va_arg(args, void *); - info->callbacks = g_slist_append(info->callbacks, cb); - break; - case GATT_OPT_CHR_VALUE_GET_HANDLE: - info->value_handle = va_arg(args, void *); - break; - case GATT_OPT_CCC_GET_HANDLE: - info->ccc_handle = va_arg(args, void *); - break; - case GATT_OPT_CHR_AUTHENTICATION: - info->authentication = va_arg(args, gatt_option); - break; - case GATT_OPT_CHR_AUTHORIZATION: - info->authorization = va_arg(args, gatt_option); - break; - case GATT_CHR_VALUE_READ: - case GATT_CHR_VALUE_WRITE: - case GATT_CHR_VALUE_BOTH: - case GATT_OPT_INVALID: - default: - error("Invalid option: %d", opt); - } - - opt = va_arg(args, gatt_option); - if (opt == GATT_OPT_CHR_UUID16 || opt == GATT_OPT_CHR_UUID) { - info = g_new0(struct gatt_info, 1); - l = g_slist_append(l, info); - } - } - - return l; -} - -static struct attribute *add_service_declaration(struct btd_adapter *adapter, - uint16_t handle, uint16_t svc, bt_uuid_t *uuid) -{ - bt_uuid_t bt_uuid; - uint8_t atval[16]; - int len; - - put_uuid_le(uuid, &atval[0]); - len = bt_uuid_len(uuid); - - bt_uuid16_create(&bt_uuid, svc); - - return attrib_db_add(adapter, handle, &bt_uuid, ATT_NONE, - ATT_NOT_PERMITTED, atval, len); -} - -static int att_read_req(int authorization, int authentication, uint8_t props) -{ - if (authorization == GATT_CHR_VALUE_READ || - authorization == GATT_CHR_VALUE_BOTH) - return ATT_AUTHORIZATION; - else if (authentication == GATT_CHR_VALUE_READ || - authentication == GATT_CHR_VALUE_BOTH) - return ATT_AUTHENTICATION; - else if (!(props & GATT_CHR_PROP_READ)) - return ATT_NOT_PERMITTED; - - return ATT_NONE; -} - -static int att_write_req(int authorization, int authentication, uint8_t props) -{ - if (authorization == GATT_CHR_VALUE_WRITE || - authorization == GATT_CHR_VALUE_BOTH) - return ATT_AUTHORIZATION; - else if (authentication == GATT_CHR_VALUE_WRITE || - authentication == GATT_CHR_VALUE_BOTH) - return ATT_AUTHENTICATION; - else if (!(props & (GATT_CHR_PROP_WRITE | - GATT_CHR_PROP_WRITE_WITHOUT_RESP))) - return ATT_NOT_PERMITTED; - - return ATT_NONE; -} - -static int find_callback(gconstpointer a, gconstpointer b) -{ - const struct attrib_cb *cb = a; - unsigned int event = GPOINTER_TO_UINT(b); - - return cb->event - event; -} - -static gboolean add_characteristic(struct btd_adapter *adapter, - uint16_t *handle, struct gatt_info *info) -{ - int read_req, write_req; - uint16_t h = *handle; - struct attribute *a; - bt_uuid_t bt_uuid; - uint8_t atval[ATT_MAX_VALUE_LEN]; - GSList *l; - - if ((info->uuid.type != BT_UUID16 && info->uuid.type != BT_UUID128) || - !info->props) { - error("Characteristic UUID or properties are missing"); - return FALSE; - } - - read_req = att_read_req(info->authorization, info->authentication, - info->props); - write_req = att_write_req(info->authorization, info->authentication, - info->props); - - /* TODO: static characteristic values are not supported, therefore a - * callback must be always provided if a read/write property is set */ - if (read_req != ATT_NOT_PERMITTED) { - gpointer reqs = GUINT_TO_POINTER(ATTRIB_READ); - - if (!g_slist_find_custom(info->callbacks, reqs, - find_callback)) { - error("Callback for read required"); - return FALSE; - } - } - - if (write_req != ATT_NOT_PERMITTED) { - gpointer reqs = GUINT_TO_POINTER(ATTRIB_WRITE); - - if (!g_slist_find_custom(info->callbacks, reqs, - find_callback)) { - error("Callback for write required"); - return FALSE; - } - } - - /* characteristic declaration */ - bt_uuid16_create(&bt_uuid, GATT_CHARAC_UUID); - atval[0] = info->props; - put_le16(h + 1, &atval[1]); - put_uuid_le(&info->uuid, &atval[3]); - if (attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE, ATT_NOT_PERMITTED, - atval, 3 + info->uuid.type / 8) == NULL) - return FALSE; - - /* characteristic value */ - a = attrib_db_add(adapter, h++, &info->uuid, read_req, write_req, - NULL, 0); - if (a == NULL) - return FALSE; - - for (l = info->callbacks; l != NULL; l = l->next) { - struct attrib_cb *cb = l->data; - - switch (cb->event) { - case ATTRIB_READ: - a->read_cb = cb->fn; - break; - case ATTRIB_WRITE: - a->write_cb = cb->fn; - break; - } - - a->cb_user_data = cb->user_data; - } - - if (info->value_handle != NULL) - *info->value_handle = a->handle; - - /* client characteristic configuration descriptor */ - if (info->props & (GATT_CHR_PROP_NOTIFY | GATT_CHR_PROP_INDICATE)) { - uint8_t cfg_val[2]; - - bt_uuid16_create(&bt_uuid, GATT_CLIENT_CHARAC_CFG_UUID); - cfg_val[0] = 0x00; - cfg_val[1] = 0x00; - a = attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE, - ATT_AUTHENTICATION, cfg_val, sizeof(cfg_val)); - if (a == NULL) - return FALSE; - - if (info->ccc_handle != NULL) - *info->ccc_handle = a->handle; - } - - *handle = h; - - return TRUE; -} - -static void free_gatt_info(void *data) -{ - struct gatt_info *info = data; - - g_slist_free_full(info->callbacks, g_free); - g_free(info); -} - -static void service_attr_del(struct btd_adapter *adapter, uint16_t start_handle, - uint16_t end_handle) -{ - uint16_t handle; - - /* For a 128-bit category primary service below handle should be checked - * for both non-zero as well as >= 0xffff. As on last iteration the - * handle will turn to 0 from 0xffff and loop will be infinite. - */ - for (handle = start_handle; (handle != 0 && handle <= end_handle); - handle++) { - if (attrib_db_del(adapter, handle) < 0) - error("Can't delete handle 0x%04x", handle); - } -} - -gboolean gatt_service_add(struct btd_adapter *adapter, uint16_t uuid, - bt_uuid_t *svc_uuid, gatt_option opt1, ...) -{ - char uuidstr[MAX_LEN_UUID_STR]; - uint16_t start_handle, h; - unsigned int size; - va_list args; - GSList *chrs, *l; - - bt_uuid_to_string(svc_uuid, uuidstr, MAX_LEN_UUID_STR); - - if (svc_uuid->type != BT_UUID16 && svc_uuid->type != BT_UUID128) { - error("Invalid service uuid: %s", uuidstr); - return FALSE; - } - - va_start(args, opt1); - chrs = parse_opts(opt1, args); - va_end(args); - - /* calculate how many attributes are necessary for this service */ - for (l = chrs, size = 1; l != NULL; l = l->next) { - struct gatt_info *info = l->data; - size += info->num_attrs; - } - - start_handle = attrib_db_find_avail(adapter, svc_uuid, size); - if (start_handle == 0) { - error("Not enough free handles to register service"); - goto fail; - } - - DBG("New service: handle 0x%04x, UUID %s, %d attributes", - start_handle, uuidstr, size); - - /* service declaration */ - h = start_handle; - if (add_service_declaration(adapter, h++, uuid, svc_uuid) == NULL) - goto fail; - - for (l = chrs; l != NULL; l = l->next) { - struct gatt_info *info = l->data; - - DBG("New characteristic: handle 0x%04x", h); - if (!add_characteristic(adapter, &h, info)) { - service_attr_del(adapter, start_handle, h - 1); - goto fail; - } - } - - g_assert(size < USHRT_MAX); - g_assert(h == 0 || (h - start_handle == (uint16_t) size)); - g_slist_free_full(chrs, free_gatt_info); - - return TRUE; - -fail: - g_slist_free_full(chrs, free_gatt_info); - return FALSE; -} diff --git a/attrib/gatt-service.h b/attrib/gatt-service.h deleted file mode 100644 index 17031d466..000000000 --- a/attrib/gatt-service.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2011 Nokia Corporation - * Copyright (C) 2011 Marcel Holtmann - * - * - */ - -typedef enum { - GATT_OPT_INVALID = 0, - - /* bt_uuid_t* value */ - GATT_OPT_CHR_UUID, - - /* a uint16 value */ - GATT_OPT_CHR_UUID16, - - GATT_OPT_CHR_PROPS, - GATT_OPT_CHR_VALUE_CB, - GATT_OPT_CHR_AUTHENTICATION, - GATT_OPT_CHR_AUTHORIZATION, - - /* Get attribute handle for characteristic value */ - GATT_OPT_CHR_VALUE_GET_HANDLE, - - /* Get handle for ccc attribute */ - GATT_OPT_CCC_GET_HANDLE, - - /* arguments for authentication/authorization */ - GATT_CHR_VALUE_READ, - GATT_CHR_VALUE_WRITE, - GATT_CHR_VALUE_BOTH, -} gatt_option; - -typedef enum { - ATTRIB_READ, - ATTRIB_WRITE, -} attrib_event_t; - -gboolean gatt_service_add(struct btd_adapter *adapter, uint16_t uuid, - bt_uuid_t *svc_uuid, gatt_option opt1, ...); diff --git a/src/adapter.c b/src/adapter.c index c49f42cfb..9772e843a 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -62,7 +62,6 @@ #include "attrib/gattrib.h" #include "attrib/att.h" #include "attrib/gatt.h" -#include "attrib-server.h" #include "gatt-database.h" #include "advertising.h" #include "adv_monitor.h" @@ -827,10 +826,6 @@ static void local_name_changed_callback(uint16_t index, uint16_t length, g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Alias"); - - attrib_gap_set(adapter, GATT_CHARAC_DEVICE_NAME, - (const uint8_t *) adapter->current_alias, - strlen(adapter->current_alias)); } static void set_local_name_complete(uint8_t status, uint16_t length, diff --git a/src/attrib-server.c b/src/attrib-server.c deleted file mode 100644 index 0063c9534..000000000 --- a/src/attrib-server.c +++ /dev/null @@ -1,1656 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2010 Nokia Corporation - * Copyright (C) 2010 Marcel Holtmann - * - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "lib/bluetooth.h" -#include "lib/sdp.h" -#include "lib/sdp_lib.h" -#include "lib/uuid.h" - -#include "btio/btio.h" -#include "log.h" -#include "backtrace.h" -#include "adapter.h" -#include "device.h" -#include "src/shared/util.h" -#include "attrib/gattrib.h" -#include "attrib/att.h" -#include "attrib/gatt.h" -#include "attrib/att-database.h" -#include "textfile.h" -#include "storage.h" - -#include "attrib-server.h" - -static GSList *servers = NULL; - -struct gatt_server { - struct btd_adapter *adapter; - GIOChannel *l2cap_io; - GIOChannel *le_io; - uint32_t gatt_sdp_handle; - uint32_t gap_sdp_handle; - GList *database; - GSList *clients; - uint16_t name_handle; - uint16_t appearance_handle; -}; - -struct gatt_channel { - GAttrib *attrib; - guint mtu; - gboolean le; - guint id; - gboolean encrypted; - struct gatt_server *server; - guint cleanup_id; - struct btd_device *device; -}; - -struct group_elem { - uint16_t handle; - uint16_t end; - uint8_t *data; - uint16_t len; -}; - -static bt_uuid_t prim_uuid = { - .type = BT_UUID16, - .value.u16 = GATT_PRIM_SVC_UUID -}; -static bt_uuid_t snd_uuid = { - .type = BT_UUID16, - .value.u16 = GATT_SND_SVC_UUID -}; -static bt_uuid_t ccc_uuid = { - .type = BT_UUID16, - .value.u16 = GATT_CLIENT_CHARAC_CFG_UUID -}; - -static void attrib_free(void *data) -{ - struct attribute *a = data; - - g_free(a->data); - g_free(a); -} - -static void channel_free(struct gatt_channel *channel) -{ - - if (channel->cleanup_id) - g_source_remove(channel->cleanup_id); - - if (channel->device) - btd_device_unref(channel->device); - - g_attrib_unref(channel->attrib); - g_free(channel); -} - -static void gatt_server_free(struct gatt_server *server) -{ - g_list_free_full(server->database, attrib_free); - - if (server->l2cap_io != NULL) { - g_io_channel_shutdown(server->l2cap_io, FALSE, NULL); - g_io_channel_unref(server->l2cap_io); - } - - if (server->le_io != NULL) { - g_io_channel_shutdown(server->le_io, FALSE, NULL); - g_io_channel_unref(server->le_io); - } - - g_slist_free_full(server->clients, (GDestroyNotify) channel_free); - - if (server->gatt_sdp_handle > 0) - adapter_service_remove(server->adapter, - server->gatt_sdp_handle); - - if (server->gap_sdp_handle > 0) - adapter_service_remove(server->adapter, server->gap_sdp_handle); - - if (server->adapter != NULL) - btd_adapter_unref(server->adapter); - - g_free(server); -} - -static int adapter_cmp_addr(gconstpointer a, gconstpointer b) -{ - const struct gatt_server *server = a; - const bdaddr_t *bdaddr = b; - - return bacmp(btd_adapter_get_address(server->adapter), bdaddr); -} - -static int adapter_cmp(gconstpointer a, gconstpointer b) -{ - const struct gatt_server *server = a; - const struct btd_adapter *adapter = b; - - if (server->adapter == adapter) - return 0; - - return -1; -} - -static struct gatt_server *find_gatt_server(const bdaddr_t *bdaddr) -{ - GSList *l; - - l = g_slist_find_custom(servers, bdaddr, adapter_cmp_addr); - if (l == NULL) { - char addr[18]; - - ba2str(bdaddr, addr); - error("No GATT server found in %s", addr); - return NULL; - } - - return l->data; -} - -static sdp_record_t *server_record_new(uuid_t *uuid, uint16_t start, uint16_t end) -{ - sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto; - uuid_t root_uuid, proto_uuid, l2cap; - sdp_record_t *record; - sdp_data_t *psm, *sh, *eh; - uint16_t lp = ATT_PSM; - - if (uuid == NULL) - return NULL; - - if (start > end) - return NULL; - - record = sdp_record_alloc(); - if (record == NULL) - return NULL; - - sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); - root = sdp_list_append(NULL, &root_uuid); - sdp_set_browse_groups(record, root); - sdp_list_free(root, NULL); - - svclass_id = sdp_list_append(NULL, uuid); - sdp_set_service_classes(record, svclass_id); - sdp_list_free(svclass_id, NULL); - - sdp_uuid16_create(&l2cap, L2CAP_UUID); - proto[0] = sdp_list_append(NULL, &l2cap); - psm = sdp_data_alloc(SDP_UINT16, &lp); - proto[0] = sdp_list_append(proto[0], psm); - apseq = sdp_list_append(NULL, proto[0]); - - sdp_uuid16_create(&proto_uuid, ATT_UUID); - proto[1] = sdp_list_append(NULL, &proto_uuid); - sh = sdp_data_alloc(SDP_UINT16, &start); - proto[1] = sdp_list_append(proto[1], sh); - eh = sdp_data_alloc(SDP_UINT16, &end); - proto[1] = sdp_list_append(proto[1], eh); - apseq = sdp_list_append(apseq, proto[1]); - - aproto = sdp_list_append(NULL, apseq); - sdp_set_access_protos(record, aproto); - - sdp_data_free(psm); - sdp_data_free(sh); - sdp_data_free(eh); - sdp_list_free(proto[0], NULL); - sdp_list_free(proto[1], NULL); - sdp_list_free(apseq, NULL); - sdp_list_free(aproto, NULL); - - return record; -} - -static int handle_cmp(gconstpointer a, gconstpointer b) -{ - const struct attribute *attrib = a; - uint16_t handle = GPOINTER_TO_UINT(b); - - return attrib->handle - handle; -} - -static int attribute_cmp(gconstpointer a1, gconstpointer a2) -{ - const struct attribute *attrib1 = a1; - const struct attribute *attrib2 = a2; - - return attrib1->handle - attrib2->handle; -} - -static struct attribute *find_svc_range(struct gatt_server *server, - uint16_t start, uint16_t *end) -{ - struct attribute *attrib; - guint h = start; - GList *l; - - if (end == NULL) - return NULL; - - l = g_list_find_custom(server->database, GUINT_TO_POINTER(h), - handle_cmp); - if (!l) - return NULL; - - attrib = l->data; - - if (bt_uuid_cmp(&attrib->uuid, &prim_uuid) != 0 && - bt_uuid_cmp(&attrib->uuid, &snd_uuid) != 0) - return NULL; - - *end = start; - - for (l = l->next; l; l = l->next) { - struct attribute *a = l->data; - - if (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 || - bt_uuid_cmp(&a->uuid, &snd_uuid) == 0) - break; - - *end = a->handle; - } - - return attrib; -} - -static uint32_t attrib_create_sdp_new(struct gatt_server *server, - uint16_t handle, const char *name) -{ - sdp_record_t *record; - struct attribute *a; - uint16_t end = 0; - uuid_t svc, gap_uuid; - - a = find_svc_range(server, handle, &end); - - if (a == NULL) - return 0; - - if (a->len == 2) - sdp_uuid16_create(&svc, get_le16(a->data)); - else if (a->len == 16) { - uint8_t be128[16]; - - /* Converting from LE to BE */ - bswap_128(a->data, be128); - sdp_uuid128_create(&svc, be128); - } else - return 0; - - record = server_record_new(&svc, handle, end); - if (record == NULL) - return 0; - - if (name != NULL) - sdp_set_info_attr(record, name, "BlueZ", NULL); - - sdp_uuid16_create(&gap_uuid, GENERIC_ACCESS_PROFILE_ID); - if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) { - sdp_set_url_attr(record, "http://www.bluez.org/", - "http://www.bluez.org/", - "http://www.bluez.org/"); - } - - if (adapter_service_add(server->adapter, record) == 0) - return record->handle; - - sdp_record_free(record); - return 0; -} - -static struct attribute *attrib_db_add_new(struct gatt_server *server, - uint16_t handle, bt_uuid_t *uuid, - int read_req, int write_req, - const uint8_t *value, size_t len) -{ - struct attribute *a; - guint h = handle; - - DBG("handle=0x%04x", handle); - - if (g_list_find_custom(server->database, GUINT_TO_POINTER(h), - handle_cmp)) - return NULL; - - a = g_new0(struct attribute, 1); - a->len = len; - a->data = g_memdup(value, len); - a->handle = handle; - a->uuid = *uuid; - a->read_req = read_req; - a->write_req = write_req; - - server->database = g_list_insert_sorted(server->database, a, - attribute_cmp); - - return a; -} - -static bool g_attrib_is_encrypted(GAttrib *attrib) -{ - BtIOSecLevel sec_level; - GIOChannel *io = g_attrib_get_channel(attrib); - - if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level, - BT_IO_OPT_INVALID)) - return FALSE; - - return sec_level > BT_IO_SEC_LOW; -} - -static uint8_t att_check_reqs(struct gatt_channel *channel, uint8_t opcode, - int reqs) -{ - /* FIXME: currently, it is assumed an encrypted link is enough for - * authentication. This will allow to enable the SMP negotiation once - * it is on upstream kernel. High security level should be mapped - * to authentication and medium to encryption permission. */ - if (!channel->encrypted) - channel->encrypted = g_attrib_is_encrypted(channel->attrib); - if (reqs == ATT_AUTHENTICATION && !channel->encrypted) - return ATT_ECODE_AUTHENTICATION; - else if (reqs == ATT_AUTHORIZATION) - return ATT_ECODE_AUTHORIZATION; - - switch (opcode) { - case ATT_OP_READ_BY_GROUP_REQ: - case ATT_OP_READ_BY_TYPE_REQ: - case ATT_OP_READ_REQ: - case ATT_OP_READ_BLOB_REQ: - case ATT_OP_READ_MULTI_REQ: - if (reqs == ATT_NOT_PERMITTED) - return ATT_ECODE_READ_NOT_PERM; - break; - case ATT_OP_PREP_WRITE_REQ: - case ATT_OP_WRITE_REQ: - case ATT_OP_WRITE_CMD: - if (reqs == ATT_NOT_PERMITTED) - return ATT_ECODE_WRITE_NOT_PERM; - break; - } - - return 0; -} - -static uint16_t read_by_group(struct gatt_channel *channel, uint16_t start, - uint16_t end, bt_uuid_t *uuid, - uint8_t *pdu, size_t len) -{ - struct att_data_list *adl; - struct attribute *a; - struct group_elem *cur, *old = NULL; - GSList *l, *groups; - GList *dl, *database; - uint16_t length, last_handle, last_size = 0; - uint8_t status; - int i; - - if (start > end || start == 0x0000) - return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, start, - ATT_ECODE_INVALID_HANDLE, pdu, len); - - /* - * Only <> and <> grouping - * types may be used in the Read By Group Type Request. - */ - - if (bt_uuid_cmp(uuid, &prim_uuid) != 0 && - bt_uuid_cmp(uuid, &snd_uuid) != 0) - return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, 0x0000, - ATT_ECODE_UNSUPP_GRP_TYPE, pdu, len); - - last_handle = end; - database = channel->server->database; - for (dl = database, groups = NULL, cur = NULL; dl; dl = dl->next) { - - a = dl->data; - - if (a->handle < start) - continue; - - if (a->handle >= end) - break; - - /* The old group ends when a new one starts */ - if (old && (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 || - bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)) { - old->end = last_handle; - old = NULL; - } - - if (bt_uuid_cmp(&a->uuid, uuid) != 0) { - /* Still inside a service, update its last handle */ - if (old) - last_handle = a->handle; - continue; - } - - if (last_size && (last_size != a->len)) - break; - - status = att_check_reqs(channel, ATT_OP_READ_BY_GROUP_REQ, - a->read_req); - - if (status == 0x00 && a->read_cb) - status = a->read_cb(a, channel->device, - a->cb_user_data); - - if (status) { - g_slist_free_full(groups, g_free); - return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, - a->handle, status, pdu, len); - } - - cur = g_new0(struct group_elem, 1); - cur->handle = a->handle; - cur->data = a->data; - cur->len = a->len; - - /* Attribute Grouping Type found */ - groups = g_slist_append(groups, cur); - - last_size = a->len; - old = cur; - last_handle = cur->handle; - } - - if (groups == NULL) - return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, start, - ATT_ECODE_ATTR_NOT_FOUND, pdu, len); - - if (dl == NULL) - cur->end = a->handle; - else - cur->end = last_handle; - - length = g_slist_length(groups); - - adl = att_data_list_alloc(length, last_size + 4); - if (adl == NULL) { - g_slist_free_full(groups, g_free); - return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, start, - ATT_ECODE_UNLIKELY, pdu, len); - } - - for (i = 0, l = groups; l; l = l->next, i++) { - uint8_t *value; - - cur = l->data; - - value = (void *) adl->data[i]; - - put_le16(cur->handle, value); - put_le16(cur->end, &value[2]); - /* Attribute Value */ - memcpy(&value[4], cur->data, cur->len); - } - - length = enc_read_by_grp_resp(adl, pdu, len); - - att_data_list_free(adl); - g_slist_free_full(groups, g_free); - - return length; -} - -static uint16_t read_by_type(struct gatt_channel *channel, uint16_t start, - uint16_t end, bt_uuid_t *uuid, - uint8_t *pdu, size_t len) -{ - struct att_data_list *adl; - GSList *l, *types; - GList *dl, *database; - struct attribute *a; - uint16_t num, length; - uint8_t status; - int i; - - if (start > end || start == 0x0000) - return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ, start, - ATT_ECODE_INVALID_HANDLE, pdu, len); - - database = channel->server->database; - for (dl = database, length = 0, types = NULL; dl; dl = dl->next) { - - a = dl->data; - - if (a->handle < start) - continue; - - if (a->handle > end) - break; - - if (bt_uuid_cmp(&a->uuid, uuid) != 0) - continue; - - status = att_check_reqs(channel, ATT_OP_READ_BY_TYPE_REQ, - a->read_req); - - if (status == 0x00 && a->read_cb) - status = a->read_cb(a, channel->device, - a->cb_user_data); - - if (status) { - g_slist_free(types); - return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ, - a->handle, status, pdu, len); - } - - /* All elements must have the same length */ - if (length == 0) - length = a->len; - else if (a->len != length) - break; - - types = g_slist_append(types, a); - } - - if (types == NULL) - return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ, start, - ATT_ECODE_ATTR_NOT_FOUND, pdu, len); - - num = g_slist_length(types); - - /* Handle length plus attribute value length */ - length += 2; - - adl = att_data_list_alloc(num, length); - if (adl == NULL) { - g_slist_free(types); - return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ, start, - ATT_ECODE_UNLIKELY, pdu, len); - } - - for (i = 0, l = types; l; i++, l = l->next) { - uint8_t *value; - - a = l->data; - - value = (void *) adl->data[i]; - - put_le16(a->handle, value); - - /* Attribute Value */ - memcpy(&value[2], a->data, a->len); - } - - length = enc_read_by_type_resp(adl, pdu, len); - - att_data_list_free(adl); - g_slist_free(types); - - return length; -} - -static uint16_t find_info(struct gatt_channel *channel, uint16_t start, - uint16_t end, uint8_t *pdu, size_t len) -{ - struct attribute *a; - struct att_data_list *adl; - GSList *l, *info; - GList *dl, *database; - uint8_t format, last_type = BT_UUID_UNSPEC; - uint16_t length, num; - int i; - - if (start > end || start == 0x0000) - return enc_error_resp(ATT_OP_FIND_INFO_REQ, start, - ATT_ECODE_INVALID_HANDLE, pdu, len); - - database = channel->server->database; - for (dl = database, info = NULL, num = 0; dl; dl = dl->next) { - a = dl->data; - - if (a->handle < start) - continue; - - if (a->handle > end) - break; - - if (last_type == BT_UUID_UNSPEC) - last_type = a->uuid.type; - - if (a->uuid.type != last_type) - break; - - info = g_slist_append(info, a); - num++; - - last_type = a->uuid.type; - } - - if (info == NULL) - return enc_error_resp(ATT_OP_FIND_INFO_REQ, start, - ATT_ECODE_ATTR_NOT_FOUND, pdu, len); - - if (last_type == BT_UUID16) { - length = 2; - format = 0x01; - } else if (last_type == BT_UUID128) { - length = 16; - format = 0x02; - } else { - g_slist_free(info); - return 0; - } - - adl = att_data_list_alloc(num, length + 2); - if (adl == NULL) { - g_slist_free(info); - return enc_error_resp(ATT_OP_FIND_INFO_REQ, start, - ATT_ECODE_UNLIKELY, pdu, len); - } - - for (i = 0, l = info; l; i++, l = l->next) { - uint8_t *value; - - a = l->data; - - value = (void *) adl->data[i]; - - put_le16(a->handle, value); - - /* Attribute Value */ - bt_uuid_to_le(&a->uuid, &value[2]); - } - - length = enc_find_info_resp(format, adl, pdu, len); - - att_data_list_free(adl); - g_slist_free(info); - - return length; -} - -static uint16_t find_by_type(struct gatt_channel *channel, uint16_t start, - uint16_t end, bt_uuid_t *uuid, - const uint8_t *value, size_t vlen, - uint8_t *opdu, size_t mtu) -{ - struct attribute *a; - struct att_range *range; - GSList *matches; - GList *dl, *database; - uint16_t len; - - if (start > end || start == 0x0000) - return enc_error_resp(ATT_OP_FIND_BY_TYPE_REQ, start, - ATT_ECODE_INVALID_HANDLE, opdu, mtu); - - /* Searching first requested handle number */ - database = channel->server->database; - for (dl = database, matches = NULL, range = NULL; dl; dl = dl->next) { - a = dl->data; - - if (a->handle < start) - continue; - - if (a->handle > end) - break; - - /* Primary service? Attribute value matches? */ - if ((bt_uuid_cmp(&a->uuid, uuid) == 0) && (a->len == vlen) && - (memcmp(a->data, value, vlen) == 0)) { - - range = g_new0(struct att_range, 1); - range->start = a->handle; - /* It is allowed to have end group handle the same as - * start handle, for groups with only one attribute. */ - range->end = a->handle; - - matches = g_slist_append(matches, range); - } else if (range) { - /* Update the last found handle or reset the pointer - * to track that a new group started: Primary or - * Secondary service. */ - if (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 || - bt_uuid_cmp(&a->uuid, &snd_uuid) == 0) - range = NULL; - else - range->end = a->handle; - } - } - - if (matches == NULL) - return enc_error_resp(ATT_OP_FIND_BY_TYPE_REQ, start, - ATT_ECODE_ATTR_NOT_FOUND, opdu, mtu); - - len = enc_find_by_type_resp(matches, opdu, mtu); - - g_slist_free_full(matches, g_free); - - return len; -} - -static int read_device_ccc(struct btd_device *device, uint16_t handle, - uint16_t *value) -{ - char *filename; - GKeyFile *key_file; - GError *gerr = NULL; - char group[6]; - char *str; - unsigned int config; - int err = 0; - - filename = btd_device_get_storage_path(device, "ccc"); - if (!filename) { - warn("Unable to get ccc storage path for device"); - return -ENOENT; - } - - key_file = g_key_file_new(); - if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { - error("Unable to load key file from %s: (%s)", filename, - gerr->message); - g_error_free(gerr); - } - - sprintf(group, "%hu", handle); - - str = g_key_file_get_string(key_file, group, "Value", NULL); - if (!str || sscanf(str, "%04X", &config) != 1) - err = -ENOENT; - else - *value = config; - - g_free(str); - g_free(filename); - g_key_file_free(key_file); - - return err; -} - -static uint16_t read_value(struct gatt_channel *channel, uint16_t handle, - uint8_t *pdu, size_t len) -{ - struct attribute *a; - uint8_t status; - GList *l; - uint16_t cccval; - guint h = handle; - - l = g_list_find_custom(channel->server->database, - GUINT_TO_POINTER(h), handle_cmp); - if (!l) - return enc_error_resp(ATT_OP_READ_REQ, handle, - ATT_ECODE_INVALID_HANDLE, pdu, len); - - a = l->data; - - if (bt_uuid_cmp(&ccc_uuid, &a->uuid) == 0 && - read_device_ccc(channel->device, handle, &cccval) == 0) { - uint8_t config[2]; - - put_le16(cccval, config); - return enc_read_resp(config, sizeof(config), pdu, len); - } - - status = att_check_reqs(channel, ATT_OP_READ_REQ, a->read_req); - - if (status == 0x00 && a->read_cb) - status = a->read_cb(a, channel->device, a->cb_user_data); - - if (status) - return enc_error_resp(ATT_OP_READ_REQ, handle, status, pdu, - len); - - return enc_read_resp(a->data, a->len, pdu, len); -} - -static uint16_t read_blob(struct gatt_channel *channel, uint16_t handle, - uint16_t offset, uint8_t *pdu, size_t len) -{ - struct attribute *a; - uint8_t status; - GList *l; - uint16_t cccval; - guint h = handle; - - l = g_list_find_custom(channel->server->database, - GUINT_TO_POINTER(h), handle_cmp); - if (!l) - return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle, - ATT_ECODE_INVALID_HANDLE, pdu, len); - - a = l->data; - - if (a->len < offset) - return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle, - ATT_ECODE_INVALID_OFFSET, pdu, len); - - if (bt_uuid_cmp(&ccc_uuid, &a->uuid) == 0 && - read_device_ccc(channel->device, handle, &cccval) == 0) { - uint8_t config[2]; - - put_le16(cccval, config); - return enc_read_blob_resp(config, sizeof(config), offset, - pdu, len); - } - - status = att_check_reqs(channel, ATT_OP_READ_BLOB_REQ, a->read_req); - - if (status == 0x00 && a->read_cb) - status = a->read_cb(a, channel->device, a->cb_user_data); - - if (status) - return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle, status, - pdu, len); - - return enc_read_blob_resp(a->data, a->len, offset, pdu, len); -} - -static uint16_t write_value(struct gatt_channel *channel, uint16_t handle, - const uint8_t *value, size_t vlen, - uint8_t *pdu, size_t len) -{ - struct attribute *a; - uint8_t status; - GList *l; - GError *gerr = NULL; - guint h = handle; - - l = g_list_find_custom(channel->server->database, - GUINT_TO_POINTER(h), handle_cmp); - if (!l) - return enc_error_resp(ATT_OP_WRITE_REQ, handle, - ATT_ECODE_INVALID_HANDLE, pdu, len); - - a = l->data; - - status = att_check_reqs(channel, ATT_OP_WRITE_REQ, a->write_req); - if (status) - return enc_error_resp(ATT_OP_WRITE_REQ, handle, status, pdu, - len); - - if (bt_uuid_cmp(&ccc_uuid, &a->uuid) != 0) { - - attrib_db_update(channel->server->adapter, handle, NULL, - value, vlen, NULL); - - if (a->write_cb) { - status = a->write_cb(a, channel->device, - a->cb_user_data); - if (status) - return enc_error_resp(ATT_OP_WRITE_REQ, handle, - status, pdu, len); - } - } else { - uint16_t cccval = get_le16(value); - char *filename; - GKeyFile *key_file; - char group[6], value[5]; - char *data; - gsize length = 0; - - filename = btd_device_get_storage_path(channel->device, "ccc"); - if (!filename) { - warn("Unable to get ccc storage path for device"); - return enc_error_resp(ATT_OP_WRITE_REQ, handle, - ATT_ECODE_WRITE_NOT_PERM, - pdu, len); - } - - key_file = g_key_file_new(); - if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) { - error("Unable to load key file from %s: (%s)", filename, - gerr->message); - g_error_free(gerr); - } - - sprintf(group, "%hu", handle); - sprintf(value, "%hX", cccval); - g_key_file_set_string(key_file, group, "Value", value); - - data = g_key_file_to_data(key_file, &length, NULL); - if (length > 0) { - create_file(filename, 0600); - if (!g_file_set_contents(filename, data, length, - &gerr)) { - error("Unable set contents for %s: (%s)", - filename, gerr->message); - g_error_free(gerr); - } - } - - g_free(data); - g_free(filename); - g_key_file_free(key_file); - } - - return enc_write_resp(pdu); -} - -static uint16_t mtu_exchange(struct gatt_channel *channel, uint16_t mtu, - uint8_t *pdu, size_t len) -{ - GError *gerr = NULL; - GIOChannel *io; - uint16_t imtu; - - if (mtu < ATT_DEFAULT_LE_MTU) - return enc_error_resp(ATT_OP_MTU_REQ, 0, - ATT_ECODE_REQ_NOT_SUPP, pdu, len); - - io = g_attrib_get_channel(channel->attrib); - - bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID); - if (gerr) { - error("bt_io_get: %s", gerr->message); - g_error_free(gerr); - return enc_error_resp(ATT_OP_MTU_REQ, 0, ATT_ECODE_UNLIKELY, - pdu, len); - } - - channel->mtu = MIN(mtu, imtu); - g_attrib_set_mtu(channel->attrib, channel->mtu); - - return enc_mtu_resp(imtu, pdu, len); -} - -static void channel_remove(struct gatt_channel *channel) -{ - channel->server->clients = g_slist_remove(channel->server->clients, - channel); - channel_free(channel); -} - -static gboolean channel_watch_cb(GIOChannel *io, GIOCondition cond, - gpointer user_data) -{ - channel_remove(user_data); - - return FALSE; -} - -static void channel_handler(const uint8_t *ipdu, uint16_t len, - gpointer user_data) -{ - struct gatt_channel *channel = user_data; - uint8_t opdu[channel->mtu]; - uint16_t length, start, end, mtu, offset; - bt_uuid_t uuid; - uint8_t status = 0; - size_t vlen; - uint8_t *value = g_attrib_get_buffer(channel->attrib, &vlen); - - DBG("op 0x%02x", ipdu[0]); - - if (len > vlen) { - error("Too much data on ATT socket"); - status = ATT_ECODE_INVALID_PDU; - goto done; - } - - switch (ipdu[0]) { - case ATT_OP_READ_BY_GROUP_REQ: - length = dec_read_by_grp_req(ipdu, len, &start, &end, &uuid); - if (length == 0) { - status = ATT_ECODE_INVALID_PDU; - goto done; - } - - length = read_by_group(channel, start, end, &uuid, opdu, - channel->mtu); - break; - case ATT_OP_READ_BY_TYPE_REQ: - length = dec_read_by_type_req(ipdu, len, &start, &end, &uuid); - if (length == 0) { - status = ATT_ECODE_INVALID_PDU; - goto done; - } - - length = read_by_type(channel, start, end, &uuid, opdu, - channel->mtu); - break; - case ATT_OP_READ_REQ: - length = dec_read_req(ipdu, len, &start); - if (length == 0) { - status = ATT_ECODE_INVALID_PDU; - goto done; - } - - length = read_value(channel, start, opdu, channel->mtu); - break; - case ATT_OP_READ_BLOB_REQ: - length = dec_read_blob_req(ipdu, len, &start, &offset); - if (length == 0) { - status = ATT_ECODE_INVALID_PDU; - goto done; - } - - length = read_blob(channel, start, offset, opdu, channel->mtu); - break; - case ATT_OP_MTU_REQ: - if (!channel->le) { - status = ATT_ECODE_REQ_NOT_SUPP; - goto done; - } - - length = dec_mtu_req(ipdu, len, &mtu); - if (length == 0) { - status = ATT_ECODE_INVALID_PDU; - goto done; - } - - length = mtu_exchange(channel, mtu, opdu, channel->mtu); - break; - case ATT_OP_FIND_INFO_REQ: - length = dec_find_info_req(ipdu, len, &start, &end); - if (length == 0) { - status = ATT_ECODE_INVALID_PDU; - goto done; - } - - length = find_info(channel, start, end, opdu, channel->mtu); - break; - case ATT_OP_WRITE_REQ: - length = dec_write_req(ipdu, len, &start, value, &vlen); - if (length == 0) { - status = ATT_ECODE_INVALID_PDU; - goto done; - } - - length = write_value(channel, start, value, vlen, opdu, - channel->mtu); - break; - case ATT_OP_WRITE_CMD: - length = dec_write_cmd(ipdu, len, &start, value, &vlen); - if (length > 0) - write_value(channel, start, value, vlen, opdu, - channel->mtu); - return; - case ATT_OP_FIND_BY_TYPE_REQ: - length = dec_find_by_type_req(ipdu, len, &start, &end, - &uuid, value, &vlen); - if (length == 0) { - status = ATT_ECODE_INVALID_PDU; - goto done; - } - - length = find_by_type(channel, start, end, &uuid, value, vlen, - opdu, channel->mtu); - break; - case ATT_OP_HANDLE_CNF: - return; - case ATT_OP_HANDLE_IND: - case ATT_OP_HANDLE_NOTIFY: - /* The attribute client is already handling these */ - return; - case ATT_OP_READ_MULTI_REQ: - case ATT_OP_PREP_WRITE_REQ: - case ATT_OP_EXEC_WRITE_REQ: - default: - DBG("Unsupported request 0x%02x", ipdu[0]); - status = ATT_ECODE_REQ_NOT_SUPP; - goto done; - } - - if (length == 0) - status = ATT_ECODE_IO; - -done: - if (status) - length = enc_error_resp(ipdu[0], 0x0000, status, opdu, - channel->mtu); - - g_attrib_send(channel->attrib, 0, opdu, length, NULL, NULL, NULL); -} - -GAttrib *attrib_from_device(struct btd_device *device) -{ - struct btd_adapter *adapter = device_get_adapter(device); - struct gatt_server *server; - GSList *l; - - l = g_slist_find_custom(servers, adapter, adapter_cmp); - if (!l) - return NULL; - - server = l->data; - - for (l = server->clients; l; l = l->next) { - struct gatt_channel *channel = l->data; - - if (channel->device == device) - return g_attrib_ref(channel->attrib); - } - - return NULL; -} - -guint attrib_channel_attach(GAttrib *attrib) -{ - struct gatt_server *server; - struct btd_device *device; - struct gatt_channel *channel; - bdaddr_t src, dst; - GIOChannel *io; - GError *gerr = NULL; - uint8_t bdaddr_type; - uint16_t cid; - guint mtu = 0; - - io = g_attrib_get_channel(attrib); - - bt_io_get(io, &gerr, - BT_IO_OPT_SOURCE_BDADDR, &src, - BT_IO_OPT_DEST_BDADDR, &dst, - BT_IO_OPT_DEST_TYPE, &bdaddr_type, - BT_IO_OPT_CID, &cid, - BT_IO_OPT_IMTU, &mtu, - BT_IO_OPT_INVALID); - if (gerr) { - error("bt_io_get: %s", gerr->message); - g_error_free(gerr); - return 0; - } - - server = find_gatt_server(&src); - if (server == NULL) - return 0; - - channel = g_new0(struct gatt_channel, 1); - channel->server = server; - - device = btd_adapter_find_device(server->adapter, &dst, bdaddr_type); - if (device == NULL) { - error("Device object not found for attrib server"); - g_free(channel); - return 0; - } - - if (!device_is_bonded(device, bdaddr_type)) { - char *filename; - - filename = btd_device_get_storage_path(device, "ccc"); - if (filename) { - unlink(filename); - g_free(filename); - } - } - - if (cid != ATT_CID) { - channel->le = FALSE; - channel->mtu = mtu; - } else { - channel->le = TRUE; - channel->mtu = ATT_DEFAULT_LE_MTU; - } - - channel->attrib = g_attrib_ref(attrib); - channel->id = g_attrib_register(channel->attrib, GATTRIB_ALL_REQS, - GATTRIB_ALL_HANDLES, channel_handler, channel, NULL); - - channel->cleanup_id = g_io_add_watch(io, G_IO_HUP, channel_watch_cb, - channel); - - channel->device = btd_device_ref(device); - - server->clients = g_slist_append(server->clients, channel); - - return channel->id; -} - -static struct gatt_channel *find_channel(guint id) -{ - GSList *l; - - for (l = servers; l; l = g_slist_next(l)) { - struct gatt_server *server = l->data; - GSList *c; - - for (c = server->clients; c; c = g_slist_next(c)) { - struct gatt_channel *channel = c->data; - - if (channel->id == id) - return channel; - } - } - - return NULL; -} - -gboolean attrib_channel_detach(GAttrib *attrib, guint id) -{ - struct gatt_channel *channel; - - channel = find_channel(id); - if (channel == NULL) - return FALSE; - - g_attrib_unregister(channel->attrib, channel->id); - channel_remove(channel); - - return TRUE; -} - -static void connect_event(GIOChannel *io, GError *gerr, void *user_data) -{ - struct btd_adapter *adapter; - struct btd_device *device; - uint8_t dst_type; - bdaddr_t src, dst; - - DBG(""); - - if (gerr) { - error("%s", gerr->message); - return; - } - - bt_io_get(io, &gerr, - BT_IO_OPT_SOURCE_BDADDR, &src, - BT_IO_OPT_DEST_BDADDR, &dst, - BT_IO_OPT_DEST_TYPE, &dst_type, - BT_IO_OPT_INVALID); - if (gerr) { - error("bt_io_get: %s", gerr->message); - g_error_free(gerr); - return; - } - - adapter = adapter_find(&src); - if (!adapter) - return; - - device = btd_adapter_get_device(adapter, &dst, dst_type); - if (!device) - return; - - device_attach_att(device, io); -} - -static gboolean register_core_services(struct gatt_server *server) -{ - uint8_t atval[256]; - bt_uuid_t uuid; - uint16_t appearance = 0x0000; - - /* GAP service: primary service definition */ - bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); - put_le16(GENERIC_ACCESS_PROFILE_ID, &atval[0]); - attrib_db_add_new(server, 0x0001, &uuid, ATT_NONE, ATT_NOT_PERMITTED, - atval, 2); - - /* GAP service: device name characteristic */ - server->name_handle = 0x0006; - bt_uuid16_create(&uuid, GATT_CHARAC_UUID); - atval[0] = GATT_CHR_PROP_READ; - put_le16(server->name_handle, &atval[1]); - put_le16(GATT_CHARAC_DEVICE_NAME, &atval[3]); - attrib_db_add_new(server, 0x0004, &uuid, ATT_NONE, ATT_NOT_PERMITTED, - atval, 5); - - /* GAP service: device name attribute */ - bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME); - attrib_db_add_new(server, server->name_handle, &uuid, ATT_NONE, - ATT_NOT_PERMITTED, NULL, 0); - - /* GAP service: device appearance characteristic */ - server->appearance_handle = 0x0008; - bt_uuid16_create(&uuid, GATT_CHARAC_UUID); - atval[0] = GATT_CHR_PROP_READ; - put_le16(server->appearance_handle, &atval[1]); - put_le16(GATT_CHARAC_APPEARANCE, &atval[3]); - attrib_db_add_new(server, 0x0007, &uuid, ATT_NONE, ATT_NOT_PERMITTED, - atval, 5); - - /* GAP service: device appearance attribute */ - bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE); - put_le16(appearance, &atval[0]); - attrib_db_add_new(server, server->appearance_handle, &uuid, ATT_NONE, - ATT_NOT_PERMITTED, atval, 2); - server->gap_sdp_handle = attrib_create_sdp_new(server, 0x0001, - "Generic Access Profile"); - if (server->gap_sdp_handle == 0) { - error("Failed to register GAP service record"); - return FALSE; - } - - /* GATT service: primary service definition */ - bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); - put_le16(GENERIC_ATTRIB_PROFILE_ID, &atval[0]); - attrib_db_add_new(server, 0x0010, &uuid, ATT_NONE, ATT_NOT_PERMITTED, - atval, 2); - - server->gatt_sdp_handle = attrib_create_sdp_new(server, 0x0010, - "Generic Attribute Profile"); - if (server->gatt_sdp_handle == 0) { - error("Failed to register GATT service record"); - return FALSE; - } - - return TRUE; -} - -int btd_adapter_gatt_server_start(struct btd_adapter *adapter) -{ - struct gatt_server *server; - GError *gerr = NULL; - const bdaddr_t *addr; - - DBG("Start GATT server in hci%d", btd_adapter_get_index(adapter)); - - server = g_new0(struct gatt_server, 1); - server->adapter = btd_adapter_ref(adapter); - - addr = btd_adapter_get_address(server->adapter); - - /* BR/EDR socket */ - server->l2cap_io = bt_io_listen(connect_event, NULL, NULL, NULL, &gerr, - BT_IO_OPT_SOURCE_BDADDR, addr, - BT_IO_OPT_PSM, ATT_PSM, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, - BT_IO_OPT_INVALID); - - if (server->l2cap_io == NULL) { - error("%s", gerr->message); - g_error_free(gerr); - gatt_server_free(server); - return -1; - } - - if (!register_core_services(server)) { - gatt_server_free(server); - return -1; - } - - /* LE socket */ - server->le_io = bt_io_listen(connect_event, NULL, - &server->le_io, NULL, &gerr, - BT_IO_OPT_SOURCE_BDADDR, addr, - BT_IO_OPT_SOURCE_TYPE, - btd_adapter_get_address_type(adapter), - BT_IO_OPT_CID, ATT_CID, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, - BT_IO_OPT_INVALID); - - if (server->le_io == NULL) { - error("%s", gerr->message); - g_error_free(gerr); - /* Doesn't have LE support, continue */ - } - - servers = g_slist_prepend(servers, server); - return 0; -} - -void btd_adapter_gatt_server_stop(struct btd_adapter *adapter) -{ - struct gatt_server *server; - GSList *l; - - l = g_slist_find_custom(servers, adapter, adapter_cmp); - if (l == NULL) - return; - - DBG("Stop GATT server in hci%d", btd_adapter_get_index(adapter)); - - server = l->data; - servers = g_slist_remove(servers, server); - gatt_server_free(server); -} - -uint32_t attrib_create_sdp(struct btd_adapter *adapter, uint16_t handle, - const char *name) -{ - GSList *l; - - l = g_slist_find_custom(servers, adapter, adapter_cmp); - if (l == NULL) - return 0; - - return attrib_create_sdp_new(l->data, handle, name); -} - -void attrib_free_sdp(struct btd_adapter *adapter, uint32_t sdp_handle) -{ - adapter_service_remove(adapter, sdp_handle); -} - -static uint16_t find_uuid16_avail(struct btd_adapter *adapter, uint16_t nitems) -{ - struct gatt_server *server; - uint16_t handle; - GSList *l; - GList *dl; - - l = g_slist_find_custom(servers, adapter, adapter_cmp); - if (l == NULL) - return 0; - - server = l->data; - if (server->database == NULL) - return 0x0001; - - for (dl = server->database, handle = 0x0001; dl; dl = dl->next) { - struct attribute *a = dl->data; - - if ((bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 || - bt_uuid_cmp(&a->uuid, &snd_uuid) == 0) && - a->handle - handle >= nitems) - /* Note: the range above excludes the current handle */ - return handle; - - if (a->len == 16 && (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 || - bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)) { - /* 128 bit UUID service definition */ - return 0; - } - - if (a->handle == 0xffff) - return 0; - - handle = a->handle + 1; - } - - if (0xffff - handle + 1 >= nitems) - return handle; - - return 0; -} - -static uint16_t find_uuid128_avail(struct btd_adapter *adapter, uint16_t nitems) -{ - uint16_t handle = 0, end = 0xffff; - struct gatt_server *server; - GList *dl; - GSList *l; - - l = g_slist_find_custom(servers, adapter, adapter_cmp); - if (l == NULL) - return 0; - - server = l->data; - if (server->database == NULL) - return 0xffff - nitems + 1; - - for (dl = g_list_last(server->database); dl; dl = dl->prev) { - struct attribute *a = dl->data; - - if (handle == 0) - handle = a->handle; - - if (bt_uuid_cmp(&a->uuid, &prim_uuid) != 0 && - bt_uuid_cmp(&a->uuid, &snd_uuid) != 0) - continue; - - if (end - handle >= nitems) - return end - nitems + 1; - - if (a->len == 2) { - /* 16 bit UUID service definition */ - return 0; - } - - if (a->handle == 0x0001) - return 0; - - end = a->handle - 1; - handle = 0; - } - - if (end - 0x0001 >= nitems) - return end - nitems + 1; - - return 0; -} - -uint16_t attrib_db_find_avail(struct btd_adapter *adapter, bt_uuid_t *svc_uuid, - uint16_t nitems) -{ - btd_assert(nitems > 0); - - if (svc_uuid->type == BT_UUID16) - return find_uuid16_avail(adapter, nitems); - else if (svc_uuid->type == BT_UUID128) - return find_uuid128_avail(adapter, nitems); - else { - char uuidstr[MAX_LEN_UUID_STR]; - - bt_uuid_to_string(svc_uuid, uuidstr, MAX_LEN_UUID_STR); - error("Service uuid: %s is neither a 16-bit nor a 128-bit uuid", - uuidstr); - return 0; - } -} - -struct attribute *attrib_db_add(struct btd_adapter *adapter, uint16_t handle, - bt_uuid_t *uuid, int read_req, - int write_req, const uint8_t *value, - size_t len) -{ - GSList *l; - - l = g_slist_find_custom(servers, adapter, adapter_cmp); - if (l == NULL) - return NULL; - - return attrib_db_add_new(l->data, handle, uuid, read_req, write_req, - value, len); -} - -int attrib_db_update(struct btd_adapter *adapter, uint16_t handle, - bt_uuid_t *uuid, const uint8_t *value, - size_t len, struct attribute **attr) -{ - struct gatt_server *server; - struct attribute *a; - GSList *l; - GList *dl; - guint h = handle; - - l = g_slist_find_custom(servers, adapter, adapter_cmp); - if (l == NULL) - return -ENOENT; - - server = l->data; - - DBG("handle=0x%04x", handle); - - dl = g_list_find_custom(server->database, GUINT_TO_POINTER(h), - handle_cmp); - if (dl == NULL) - return -ENOENT; - - a = dl->data; - - a->data = g_try_realloc(a->data, len); - if (len && a->data == NULL) - return -ENOMEM; - - a->len = len; - memcpy(a->data, value, len); - - if (uuid != NULL) - a->uuid = *uuid; - - if (attr) - *attr = a; - - return 0; -} - -int attrib_db_del(struct btd_adapter *adapter, uint16_t handle) -{ - struct gatt_server *server; - struct attribute *a; - GSList *l; - GList *dl; - guint h = handle; - - l = g_slist_find_custom(servers, adapter, adapter_cmp); - if (l == NULL) - return -ENOENT; - - server = l->data; - - DBG("handle=0x%04x", handle); - - dl = g_list_find_custom(server->database, GUINT_TO_POINTER(h), - handle_cmp); - if (dl == NULL) - return -ENOENT; - - a = dl->data; - server->database = g_list_remove(server->database, a); - g_free(a->data); - g_free(a); - - return 0; -} - -int attrib_gap_set(struct btd_adapter *adapter, uint16_t uuid, - const uint8_t *value, size_t len) -{ - struct gatt_server *server; - uint16_t handle; - GSList *l; - - l = g_slist_find_custom(servers, adapter, adapter_cmp); - if (l == NULL) - return -ENOENT; - - server = l->data; - - /* FIXME: Missing Privacy and Reconnection Address */ - - switch (uuid) { - case GATT_CHARAC_DEVICE_NAME: - handle = server->name_handle; - break; - case GATT_CHARAC_APPEARANCE: - handle = server->appearance_handle; - break; - default: - return -ENOSYS; - } - - return attrib_db_update(adapter, handle, NULL, value, len, NULL); -} diff --git a/src/attrib-server.h b/src/attrib-server.h deleted file mode 100644 index 7cdbe3b96..000000000 --- a/src/attrib-server.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2010 Nokia Corporation - * Copyright (C) 2010 Marcel Holtmann - * - * - */ - -uint16_t attrib_db_find_avail(struct btd_adapter *adapter, bt_uuid_t *svc_uuid, - uint16_t nitems); -struct attribute *attrib_db_add(struct btd_adapter *adapter, uint16_t handle, - bt_uuid_t *uuid, int read_req, - int write_req, const uint8_t *value, - size_t len); -int attrib_db_update(struct btd_adapter *adapter, uint16_t handle, - bt_uuid_t *uuid, const uint8_t *value, - size_t len, struct attribute **attr); -int attrib_db_del(struct btd_adapter *adapter, uint16_t handle); -int attrib_gap_set(struct btd_adapter *adapter, uint16_t uuid, - const uint8_t *value, size_t len); -uint32_t attrib_create_sdp(struct btd_adapter *adapter, uint16_t handle, - const char *name); -void attrib_free_sdp(struct btd_adapter *adapter, uint32_t sdp_handle); -GAttrib *attrib_from_device(struct btd_device *device); -guint attrib_channel_attach(GAttrib *attrib); -gboolean attrib_channel_detach(GAttrib *attrib, guint id); diff --git a/src/device.c b/src/device.c index 982b7979e..f2447c478 100644 --- a/src/device.c +++ b/src/device.c @@ -62,7 +62,6 @@ #include "agent.h" #include "textfile.h" #include "storage.h" -#include "attrib-server.h" #include "eir.h" #define DISCONNECT_TIMER 2