summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am4
-rw-r--r--attrib/gatt-service.c362
-rw-r--r--attrib/gatt-service.h44
-rw-r--r--src/adapter.c5
-rw-r--r--src/attrib-server.c1656
-rw-r--r--src/attrib-server.h29
-rw-r--r--src/device.c1
7 files changed, 1 insertions, 2100 deletions
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 <marcel@holtmann.org>
- *
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-
-#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 <marcel@holtmann.org>
- *
- *
- */
-
-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 <marcel@holtmann.org>
- *
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <string.h>
-#include <unistd.h>
-#include <glib.h>
-#include <sys/stat.h>
-
-#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 <<Primary Service>> and <<Secondary Service>> 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 <marcel@holtmann.org>
- *
- *
- */
-
-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