mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-15 00:04:29 +08:00
core: Introduce btd_gatt_database
This patch introduces src/gatt-database.* which handles incoming ATT connections, manages per-adapter shared/gatt-db instances, and routes connections to the corresponding device object. This is the layer that will perform all the CCC management and Service Changed handling.
This commit is contained in:
parent
66b407e1f2
commit
d330725947
@ -167,6 +167,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
|
||||
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 \
|
||||
src/textfile.h src/textfile.c \
|
||||
|
@ -302,6 +302,7 @@ static void dev_class_changed_callback(uint16_t index, uint16_t length,
|
||||
appearance[1] = rp->val[1] & 0x1f; /* removes service class */
|
||||
appearance[2] = rp->val[2];
|
||||
|
||||
/* TODO: Do this through btd_gatt_database instead */
|
||||
attrib_gap_set(adapter, GATT_CHARAC_APPEARANCE, appearance, 2);
|
||||
}
|
||||
|
||||
@ -4014,6 +4015,7 @@ static void convert_sdp_entry(char *key, char *value, void *user_data)
|
||||
if (record_has_uuid(rec, att_uuid))
|
||||
goto failed;
|
||||
|
||||
/* TODO: Do this through btd_gatt_database */
|
||||
if (!gatt_parse_record(rec, &uuid, &psm, &start, &end))
|
||||
goto failed;
|
||||
|
||||
@ -6590,6 +6592,7 @@ static int adapter_register(struct btd_adapter *adapter)
|
||||
agent_unref(agent);
|
||||
}
|
||||
|
||||
/* TODO: Migrate to use btd_gatt_database */
|
||||
btd_adapter_gatt_server_start(adapter);
|
||||
|
||||
load_config(adapter);
|
||||
|
680
src/gatt-database.c
Normal file
680
src/gatt-database.c
Normal file
@ -0,0 +1,680 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2015 Google Inc.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/uuid.h"
|
||||
#include "btio/btio.h"
|
||||
#include "src/shared/util.h"
|
||||
#include "src/shared/queue.h"
|
||||
#include "src/shared/att.h"
|
||||
#include "src/shared/gatt-db.h"
|
||||
#include "src/shared/gatt-server.h"
|
||||
#include "log.h"
|
||||
#include "adapter.h"
|
||||
#include "device.h"
|
||||
#include "gatt-database.h"
|
||||
|
||||
#ifndef ATT_CID
|
||||
#define ATT_CID 4
|
||||
#endif
|
||||
|
||||
#define UUID_GAP 0x1800
|
||||
#define UUID_GATT 0x1801
|
||||
|
||||
struct btd_gatt_database {
|
||||
struct btd_adapter *adapter;
|
||||
struct gatt_db *db;
|
||||
unsigned int db_id;
|
||||
GIOChannel *le_io;
|
||||
struct queue *device_states;
|
||||
struct gatt_db_attribute *svc_chngd;
|
||||
struct gatt_db_attribute *svc_chngd_ccc;
|
||||
};
|
||||
|
||||
struct device_state {
|
||||
bdaddr_t bdaddr;
|
||||
uint8_t bdaddr_type;
|
||||
struct queue *ccc_states;
|
||||
};
|
||||
|
||||
struct ccc_state {
|
||||
uint16_t handle;
|
||||
uint8_t value[2];
|
||||
};
|
||||
|
||||
struct device_info {
|
||||
bdaddr_t bdaddr;
|
||||
uint8_t bdaddr_type;
|
||||
};
|
||||
|
||||
static bool dev_state_match(const void *a, const void *b)
|
||||
{
|
||||
const struct device_state *dev_state = a;
|
||||
const struct device_info *dev_info = b;
|
||||
|
||||
return bacmp(&dev_state->bdaddr, &dev_info->bdaddr) == 0 &&
|
||||
dev_state->bdaddr_type == dev_info->bdaddr_type;
|
||||
}
|
||||
|
||||
static struct device_state *
|
||||
find_device_state(struct btd_gatt_database *database, bdaddr_t *bdaddr,
|
||||
uint8_t bdaddr_type)
|
||||
{
|
||||
struct device_info dev_info;
|
||||
|
||||
memset(&dev_info, 0, sizeof(dev_info));
|
||||
|
||||
bacpy(&dev_info.bdaddr, bdaddr);
|
||||
dev_info.bdaddr_type = bdaddr_type;
|
||||
|
||||
return queue_find(database->device_states, dev_state_match, &dev_info);
|
||||
}
|
||||
|
||||
static bool ccc_state_match(const void *a, const void *b)
|
||||
{
|
||||
const struct ccc_state *ccc = a;
|
||||
uint16_t handle = PTR_TO_UINT(b);
|
||||
|
||||
return ccc->handle == handle;
|
||||
}
|
||||
|
||||
static struct ccc_state *find_ccc_state(struct device_state *dev_state,
|
||||
uint16_t handle)
|
||||
{
|
||||
return queue_find(dev_state->ccc_states, ccc_state_match,
|
||||
UINT_TO_PTR(handle));
|
||||
}
|
||||
|
||||
static struct device_state *device_state_create(bdaddr_t *bdaddr,
|
||||
uint8_t bdaddr_type)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
|
||||
dev_state = new0(struct device_state, 1);
|
||||
if (!dev_state)
|
||||
return NULL;
|
||||
|
||||
dev_state->ccc_states = queue_new();
|
||||
if (!dev_state->ccc_states) {
|
||||
free(dev_state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bacpy(&dev_state->bdaddr, bdaddr);
|
||||
dev_state->bdaddr_type = bdaddr_type;
|
||||
|
||||
return dev_state;
|
||||
}
|
||||
|
||||
static struct device_state *get_device_state(struct btd_gatt_database *database,
|
||||
bdaddr_t *bdaddr,
|
||||
uint8_t bdaddr_type)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
|
||||
/*
|
||||
* Find and return a device state. If a matching state doesn't exist,
|
||||
* then create a new one.
|
||||
*/
|
||||
dev_state = find_device_state(database, bdaddr, bdaddr_type);
|
||||
if (dev_state)
|
||||
return dev_state;
|
||||
|
||||
dev_state = device_state_create(bdaddr, bdaddr_type);
|
||||
if (!dev_state)
|
||||
return NULL;
|
||||
|
||||
queue_push_tail(database->device_states, dev_state);
|
||||
|
||||
return dev_state;
|
||||
}
|
||||
|
||||
static struct ccc_state *get_ccc_state(struct btd_gatt_database *database,
|
||||
bdaddr_t *bdaddr,
|
||||
uint8_t bdaddr_type,
|
||||
uint16_t handle)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
struct ccc_state *ccc;
|
||||
|
||||
dev_state = get_device_state(database, bdaddr, bdaddr_type);
|
||||
if (!dev_state)
|
||||
return NULL;
|
||||
|
||||
ccc = find_ccc_state(dev_state, handle);
|
||||
if (ccc)
|
||||
return ccc;
|
||||
|
||||
ccc = new0(struct ccc_state, 1);
|
||||
if (!ccc)
|
||||
return NULL;
|
||||
|
||||
ccc->handle = handle;
|
||||
queue_push_tail(dev_state->ccc_states, ccc);
|
||||
|
||||
return ccc;
|
||||
}
|
||||
|
||||
static void device_state_free(void *data)
|
||||
{
|
||||
struct device_state *state = data;
|
||||
|
||||
queue_destroy(state->ccc_states, free);
|
||||
free(state);
|
||||
}
|
||||
|
||||
static void gatt_database_free(void *data)
|
||||
{
|
||||
struct btd_gatt_database *database = data;
|
||||
|
||||
if (database->le_io) {
|
||||
g_io_channel_shutdown(database->le_io, FALSE, NULL);
|
||||
g_io_channel_unref(database->le_io);
|
||||
}
|
||||
|
||||
/* TODO: Persistently store CCC states before freeing them */
|
||||
queue_destroy(database->device_states, device_state_free);
|
||||
gatt_db_unregister(database->db, database->db_id);
|
||||
gatt_db_unref(database->db);
|
||||
btd_adapter_unref(database->adapter);
|
||||
free(database);
|
||||
}
|
||||
|
||||
static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
|
||||
{
|
||||
struct btd_adapter *adapter;
|
||||
struct btd_device *device;
|
||||
uint8_t dst_type;
|
||||
bdaddr_t src, dst;
|
||||
|
||||
DBG("New incoming LE ATT connection");
|
||||
|
||||
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;
|
||||
|
||||
/* TODO: create bt_gatt_server instance */
|
||||
|
||||
device_attach_att(device, io);
|
||||
}
|
||||
|
||||
static void gap_device_name_read_cb(struct gatt_db_attribute *attrib,
|
||||
unsigned int id, uint16_t offset,
|
||||
uint8_t opcode, struct bt_att *att,
|
||||
void *user_data)
|
||||
{
|
||||
struct btd_gatt_database *database = user_data;
|
||||
uint8_t error = 0;
|
||||
size_t len = 0;
|
||||
const uint8_t *value = NULL;
|
||||
const char *device_name;
|
||||
|
||||
DBG("GAP Device Name read request\n");
|
||||
|
||||
device_name = btd_adapter_get_name(database->adapter);
|
||||
len = strlen(device_name);
|
||||
|
||||
if (offset > len) {
|
||||
error = BT_ATT_ERROR_INVALID_OFFSET;
|
||||
goto done;
|
||||
}
|
||||
|
||||
len -= offset;
|
||||
value = len ? (const uint8_t *) &device_name[offset] : NULL;
|
||||
|
||||
done:
|
||||
gatt_db_attribute_read_result(attrib, id, error, value, len);
|
||||
}
|
||||
|
||||
static void gap_appearance_read_cb(struct gatt_db_attribute *attrib,
|
||||
unsigned int id, uint16_t offset,
|
||||
uint8_t opcode, struct bt_att *att,
|
||||
void *user_data)
|
||||
{
|
||||
struct btd_gatt_database *database = user_data;
|
||||
uint8_t error = 0;
|
||||
size_t len = 2;
|
||||
const uint8_t *value = NULL;
|
||||
uint8_t appearance[2];
|
||||
uint32_t dev_class;
|
||||
|
||||
DBG("GAP Appearance read request\n");
|
||||
|
||||
dev_class = btd_adapter_get_class(database->adapter);
|
||||
|
||||
if (offset > 2) {
|
||||
error = BT_ATT_ERROR_INVALID_OFFSET;
|
||||
goto done;
|
||||
}
|
||||
|
||||
appearance[0] = dev_class & 0x00ff;
|
||||
appearance[1] = (dev_class >> 8) & 0x001f;
|
||||
|
||||
len -= offset;
|
||||
value = len ? &appearance[offset] : NULL;
|
||||
|
||||
done:
|
||||
gatt_db_attribute_read_result(attrib, id, error, value, len);
|
||||
}
|
||||
|
||||
static void populate_gap_service(struct btd_gatt_database *database)
|
||||
{
|
||||
bt_uuid_t uuid;
|
||||
struct gatt_db_attribute *service;
|
||||
|
||||
/* Add the GAP service */
|
||||
bt_uuid16_create(&uuid, UUID_GAP);
|
||||
service = gatt_db_add_service(database->db, &uuid, true, 5);
|
||||
|
||||
/*
|
||||
* Device Name characteristic.
|
||||
*/
|
||||
bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
|
||||
gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ,
|
||||
BT_GATT_CHRC_PROP_READ,
|
||||
gap_device_name_read_cb,
|
||||
NULL, database);
|
||||
|
||||
/*
|
||||
* Device Appearance characteristic.
|
||||
*/
|
||||
bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
|
||||
gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ,
|
||||
BT_GATT_CHRC_PROP_READ,
|
||||
gap_appearance_read_cb,
|
||||
NULL, database);
|
||||
|
||||
gatt_db_service_set_active(service, true);
|
||||
}
|
||||
|
||||
static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
|
||||
{
|
||||
GIOChannel *io = NULL;
|
||||
GError *gerr = NULL;
|
||||
|
||||
io = g_io_channel_unix_new(bt_att_get_fd(att));
|
||||
if (!io)
|
||||
return false;
|
||||
|
||||
bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst,
|
||||
BT_IO_OPT_DEST_TYPE, dst_type,
|
||||
BT_IO_OPT_INVALID);
|
||||
if (gerr) {
|
||||
error("gatt: bt_io_get: %s", gerr->message);
|
||||
g_error_free(gerr);
|
||||
g_io_channel_unref(io);
|
||||
return false;
|
||||
}
|
||||
|
||||
g_io_channel_unref(io);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
|
||||
unsigned int id, uint16_t offset,
|
||||
uint8_t opcode, struct bt_att *att,
|
||||
void *user_data)
|
||||
{
|
||||
struct btd_gatt_database *database = user_data;
|
||||
struct ccc_state *ccc;
|
||||
uint16_t handle;
|
||||
uint8_t ecode = 0;
|
||||
const uint8_t *value = NULL;
|
||||
size_t len = 0;
|
||||
bdaddr_t bdaddr;
|
||||
uint8_t bdaddr_type;
|
||||
|
||||
handle = gatt_db_attribute_get_handle(attrib);
|
||||
|
||||
DBG("CCC read called for handle: 0x%04x", handle);
|
||||
|
||||
if (offset > 2) {
|
||||
ecode = BT_ATT_ERROR_INVALID_OFFSET;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
|
||||
ecode = BT_ATT_ERROR_UNLIKELY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ccc = get_ccc_state(database, &bdaddr, bdaddr_type, handle);
|
||||
if (!ccc) {
|
||||
ecode = BT_ATT_ERROR_UNLIKELY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
len -= offset;
|
||||
value = len ? &ccc->value[offset] : NULL;
|
||||
|
||||
done:
|
||||
gatt_db_attribute_read_result(attrib, id, ecode, value, len);
|
||||
}
|
||||
|
||||
static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
|
||||
unsigned int id, uint16_t offset,
|
||||
const uint8_t *value, size_t len,
|
||||
uint8_t opcode, struct bt_att *att,
|
||||
void *user_data)
|
||||
{
|
||||
struct btd_gatt_database *database = user_data;
|
||||
struct ccc_state *ccc;
|
||||
uint16_t handle;
|
||||
uint8_t ecode = 0;
|
||||
bdaddr_t bdaddr;
|
||||
uint8_t bdaddr_type;
|
||||
|
||||
handle = gatt_db_attribute_get_handle(attrib);
|
||||
|
||||
DBG("CCC read called for handle: 0x%04x", handle);
|
||||
|
||||
if (!value || len != 2) {
|
||||
ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (offset > 2) {
|
||||
ecode = BT_ATT_ERROR_INVALID_OFFSET;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
|
||||
ecode = BT_ATT_ERROR_UNLIKELY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ccc = get_ccc_state(database, &bdaddr, bdaddr_type, handle);
|
||||
if (!ccc) {
|
||||
ecode = BT_ATT_ERROR_UNLIKELY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Perform this after checking with a callback to the upper
|
||||
* layer.
|
||||
*/
|
||||
ccc->value[0] = value[0];
|
||||
ccc->value[1] = value[1];
|
||||
|
||||
done:
|
||||
gatt_db_attribute_write_result(attrib, id, ecode);
|
||||
}
|
||||
|
||||
static struct gatt_db_attribute *
|
||||
gatt_database_add_ccc(struct btd_gatt_database *database,
|
||||
uint16_t service_handle)
|
||||
{
|
||||
struct gatt_db_attribute *service;
|
||||
bt_uuid_t uuid;
|
||||
|
||||
if (!database || !service_handle)
|
||||
return NULL;
|
||||
|
||||
service = gatt_db_get_attribute(database->db, service_handle);
|
||||
if (!service) {
|
||||
error("No service exists with handle: 0x%04x", service_handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
|
||||
return gatt_db_service_add_descriptor(service, &uuid,
|
||||
BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
|
||||
gatt_ccc_read_cb, gatt_ccc_write_cb, database);
|
||||
}
|
||||
|
||||
static void populate_gatt_service(struct btd_gatt_database *database)
|
||||
{
|
||||
bt_uuid_t uuid;
|
||||
struct gatt_db_attribute *service;
|
||||
uint16_t start_handle;
|
||||
|
||||
/* Add the GATT service */
|
||||
bt_uuid16_create(&uuid, UUID_GATT);
|
||||
service = gatt_db_add_service(database->db, &uuid, true, 4);
|
||||
gatt_db_attribute_get_service_handles(service, &start_handle, NULL);
|
||||
|
||||
bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
|
||||
database->svc_chngd = gatt_db_service_add_characteristic(service, &uuid,
|
||||
BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_INDICATE,
|
||||
NULL, NULL, database);
|
||||
|
||||
database->svc_chngd_ccc = gatt_database_add_ccc(database, start_handle);
|
||||
|
||||
gatt_db_service_set_active(service, true);
|
||||
}
|
||||
|
||||
static void register_core_services(struct btd_gatt_database *database)
|
||||
{
|
||||
populate_gap_service(database);
|
||||
populate_gatt_service(database);
|
||||
}
|
||||
|
||||
struct not_data {
|
||||
struct btd_gatt_database *database;
|
||||
uint16_t handle, ccc_handle;
|
||||
const uint8_t *value;
|
||||
uint16_t len;
|
||||
bool indicate;
|
||||
};
|
||||
|
||||
static void send_notification_to_device(void *data, void *user_data)
|
||||
{
|
||||
struct device_state *device_state = data;
|
||||
struct not_data *not_data = user_data;
|
||||
struct ccc_state *ccc;
|
||||
struct btd_device *device;
|
||||
|
||||
ccc = find_ccc_state(device_state, not_data->ccc_handle);
|
||||
if (!ccc)
|
||||
return;
|
||||
|
||||
if (!ccc->value[0] || (not_data->indicate && !(ccc->value[0] & 0x02)))
|
||||
return;
|
||||
|
||||
device = btd_adapter_get_device(not_data->database->adapter,
|
||||
&device_state->bdaddr,
|
||||
device_state->bdaddr_type);
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
/*
|
||||
* TODO: Notify device via bt_gatt_server
|
||||
*/
|
||||
}
|
||||
|
||||
static void send_notification_to_devices(struct btd_gatt_database *database,
|
||||
uint16_t handle, const uint8_t *value,
|
||||
uint16_t len, uint16_t ccc_handle,
|
||||
bool indicate)
|
||||
{
|
||||
struct not_data not_data;
|
||||
|
||||
memset(¬_data, 0, sizeof(not_data));
|
||||
|
||||
not_data.database = database;
|
||||
not_data.handle = handle;
|
||||
not_data.ccc_handle = ccc_handle;
|
||||
not_data.value = value;
|
||||
not_data.len = len;
|
||||
not_data.indicate = indicate;
|
||||
|
||||
queue_foreach(database->device_states, send_notification_to_device,
|
||||
¬_data);
|
||||
}
|
||||
|
||||
static void send_service_changed(struct btd_gatt_database *database,
|
||||
struct gatt_db_attribute *attrib)
|
||||
{
|
||||
uint16_t start, end;
|
||||
uint8_t value[4];
|
||||
uint16_t handle, ccc_handle;
|
||||
|
||||
if (!gatt_db_attribute_get_service_handles(attrib, &start, &end)) {
|
||||
error("Failed to obtain changed service handles");
|
||||
return;
|
||||
}
|
||||
|
||||
handle = gatt_db_attribute_get_handle(database->svc_chngd);
|
||||
ccc_handle = gatt_db_attribute_get_handle(database->svc_chngd_ccc);
|
||||
|
||||
if (!handle || !ccc_handle) {
|
||||
error("Failed to obtain handles for \"Service Changed\""
|
||||
" characteristic");
|
||||
return;
|
||||
}
|
||||
|
||||
put_le16(start, value);
|
||||
put_le16(end, value + 2);
|
||||
|
||||
send_notification_to_devices(database, handle, value, sizeof(value),
|
||||
ccc_handle, true);
|
||||
}
|
||||
|
||||
static void gatt_db_service_added(struct gatt_db_attribute *attrib,
|
||||
void *user_data)
|
||||
{
|
||||
struct btd_gatt_database *database = user_data;
|
||||
|
||||
DBG("GATT Service added to local database");
|
||||
|
||||
send_service_changed(database, attrib);
|
||||
}
|
||||
|
||||
static bool ccc_match_service(const void *data, const void *match_data)
|
||||
{
|
||||
const struct ccc_state *ccc = data;
|
||||
const struct gatt_db_attribute *attrib = match_data;
|
||||
uint16_t start, end;
|
||||
|
||||
if (!gatt_db_attribute_get_service_handles(attrib, &start, &end))
|
||||
return false;
|
||||
|
||||
return ccc->handle >= start && ccc->handle <= end;
|
||||
}
|
||||
|
||||
static void remove_device_ccc(void *data, void *user_data)
|
||||
{
|
||||
struct device_state *state = data;
|
||||
|
||||
queue_remove_all(state->ccc_states, ccc_match_service, user_data, free);
|
||||
}
|
||||
|
||||
static void gatt_db_service_removed(struct gatt_db_attribute *attrib,
|
||||
void *user_data)
|
||||
{
|
||||
struct btd_gatt_database *database = user_data;
|
||||
|
||||
DBG("Local GATT service removed");
|
||||
|
||||
send_service_changed(database, attrib);
|
||||
|
||||
queue_foreach(database->device_states, remove_device_ccc, attrib);
|
||||
}
|
||||
|
||||
struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
|
||||
{
|
||||
struct btd_gatt_database *database;
|
||||
GError *gerr = NULL;
|
||||
const bdaddr_t *addr;
|
||||
|
||||
if (!adapter)
|
||||
return NULL;
|
||||
|
||||
database = new0(struct btd_gatt_database, 1);
|
||||
if (!database)
|
||||
return NULL;
|
||||
|
||||
database->adapter = btd_adapter_ref(adapter);
|
||||
database->db = gatt_db_new();
|
||||
if (!database->db)
|
||||
goto fail;
|
||||
|
||||
database->device_states = queue_new();
|
||||
if (!database->device_states)
|
||||
goto fail;
|
||||
|
||||
database->db_id = gatt_db_register(database->db, gatt_db_service_added,
|
||||
gatt_db_service_removed,
|
||||
database, NULL);
|
||||
if (!database->db_id)
|
||||
goto fail;
|
||||
|
||||
addr = btd_adapter_get_address(adapter);
|
||||
database->le_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
|
||||
BT_IO_OPT_SOURCE_BDADDR, addr,
|
||||
BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
|
||||
BT_IO_OPT_CID, ATT_CID,
|
||||
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
|
||||
BT_IO_OPT_INVALID);
|
||||
if (!database->le_io) {
|
||||
error("Failed to start listening: %s", gerr->message);
|
||||
g_error_free(gerr);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
register_core_services(database);
|
||||
|
||||
return database;
|
||||
|
||||
fail:
|
||||
gatt_database_free(database);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void btd_gatt_database_destroy(struct btd_gatt_database *database)
|
||||
{
|
||||
if (!database)
|
||||
return;
|
||||
|
||||
gatt_database_free(database);
|
||||
}
|
||||
|
||||
struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database)
|
||||
{
|
||||
if (!database)
|
||||
return NULL;
|
||||
|
||||
return database->db;
|
||||
}
|
25
src/gatt-database.h
Normal file
25
src/gatt-database.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2015 Google Inc.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
struct btd_gatt_database;
|
||||
|
||||
struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter);
|
||||
void btd_gatt_database_destroy(struct btd_gatt_database *database);
|
||||
|
||||
struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database);
|
Loading…
Reference in New Issue
Block a user