mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-12-22 10:23:27 +08:00
350107b3c4
We should use consistent names for client notify functions.
1237 lines
30 KiB
C
1237 lines
30 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2014 Intel Corporation. All rights reserved.
|
|
*
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <glib.h>
|
|
#include <errno.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include "ipc.h"
|
|
#include "ipc-common.h"
|
|
#include "lib/sdp.h"
|
|
#include "lib/uuid.h"
|
|
#include "bluetooth.h"
|
|
#include "gatt.h"
|
|
#include "src/log.h"
|
|
#include "hal-msg.h"
|
|
#include "utils.h"
|
|
#include "src/shared/util.h"
|
|
#include "src/shared/queue.h"
|
|
#include "attrib/gattrib.h"
|
|
#include "attrib/att.h"
|
|
#include "attrib/gatt.h"
|
|
#include "btio/btio.h"
|
|
|
|
struct gatt_client {
|
|
int32_t id;
|
|
uint8_t uuid[16];
|
|
};
|
|
|
|
struct gatt_device {
|
|
bdaddr_t bdaddr;
|
|
uint8_t bdaddr_type;
|
|
|
|
struct queue *clients;
|
|
|
|
bool connect_ready;
|
|
int32_t conn_id;
|
|
|
|
GAttrib *attrib;
|
|
GIOChannel *att_io;
|
|
struct queue *services;
|
|
|
|
guint watch_id;
|
|
};
|
|
|
|
static struct ipc *hal_ipc = NULL;
|
|
static bdaddr_t adapter_addr;
|
|
static bool scanning = false;
|
|
|
|
static struct queue *gatt_clients = NULL;
|
|
static struct queue *conn_list = NULL; /* Connected devices */
|
|
static struct queue *conn_wait_queue = NULL; /* Devs waiting to connect */
|
|
|
|
static void bt_le_discovery_stop_cb(void);
|
|
|
|
static bool match_client_by_uuid(const void *data, const void *user_data)
|
|
{
|
|
const uint8_t *exp_uuid = user_data;
|
|
const struct gatt_client *client = data;
|
|
|
|
return !memcmp(exp_uuid, client->uuid, sizeof(client->uuid));
|
|
}
|
|
|
|
static bool match_client_by_id(const void *data, const void *user_data)
|
|
{
|
|
int32_t exp_id = PTR_TO_INT(user_data);
|
|
const struct gatt_client *client = data;
|
|
|
|
return client->id == exp_id;
|
|
}
|
|
|
|
static bool match_by_value(const void *data, const void *user_data)
|
|
{
|
|
return data == user_data;
|
|
}
|
|
|
|
static bool match_dev_by_bdaddr(const void *data, const void *user_data)
|
|
{
|
|
const struct gatt_device *dev = data;
|
|
const bdaddr_t *addr = user_data;
|
|
|
|
return !bacmp(&dev->bdaddr, addr);
|
|
}
|
|
|
|
static bool match_dev_connect_ready(const void *data, const void *user_data)
|
|
{
|
|
const struct gatt_device *dev = data;
|
|
|
|
return dev->connect_ready;
|
|
}
|
|
|
|
static bool match_dev_by_conn_id(const void *data, const void *user_data)
|
|
{
|
|
const struct gatt_device *dev = data;
|
|
const int32_t conn_id = PTR_TO_INT(user_data);
|
|
|
|
return dev->conn_id == conn_id;
|
|
}
|
|
|
|
static void destroy_device(void *data)
|
|
{
|
|
struct gatt_device *dev = data;
|
|
|
|
queue_destroy(dev->clients, NULL);
|
|
queue_destroy(dev->services, free);
|
|
free(dev);
|
|
}
|
|
|
|
static void handle_client_register(const void *buf, uint16_t len)
|
|
{
|
|
const struct hal_cmd_gatt_client_register *cmd = buf;
|
|
struct hal_ev_gatt_client_register_client ev;
|
|
struct gatt_client *client;
|
|
static int32_t client_cnt = 1;
|
|
uint8_t status;
|
|
|
|
DBG("");
|
|
|
|
if (!cmd->uuid) {
|
|
error("gatt: no uuid received");
|
|
status = HAL_STATUS_FAILED;
|
|
goto failed;
|
|
}
|
|
|
|
if (queue_find(gatt_clients, match_client_by_uuid, &cmd->uuid)) {
|
|
error("gatt: client uuid is already on list");
|
|
status = HAL_STATUS_FAILED;
|
|
goto failed;
|
|
}
|
|
|
|
client = new0(struct gatt_client, 1);
|
|
|
|
memcpy(client->uuid, cmd->uuid, sizeof(client->uuid));
|
|
|
|
client->id = client_cnt++;
|
|
|
|
queue_push_head(gatt_clients, client);
|
|
|
|
status = HAL_STATUS_SUCCESS;
|
|
|
|
ev.status = status;
|
|
ev.client_if = client->id;
|
|
memcpy(ev.app_uuid, client->uuid, sizeof(client->uuid));
|
|
|
|
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_EV_GATT_CLIENT_REGISTER_CLIENT, sizeof(ev), &ev);
|
|
|
|
failed:
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_REGISTER, status);
|
|
}
|
|
|
|
static void handle_client_unregister(const void *buf, uint16_t len)
|
|
{
|
|
const struct hal_cmd_gatt_client_unregister *cmd = buf;
|
|
uint8_t status;
|
|
struct gatt_client *cl;
|
|
|
|
DBG("");
|
|
|
|
cl = queue_remove_if(gatt_clients, match_client_by_id,
|
|
INT_TO_PTR(cmd->client_if));
|
|
if (!cl) {
|
|
error("gatt: client_if=%d not found", cmd->client_if);
|
|
status = HAL_STATUS_FAILED;
|
|
goto failed;
|
|
}
|
|
|
|
free(cl);
|
|
status = HAL_STATUS_SUCCESS;
|
|
|
|
failed:
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_UNREGISTER, status);
|
|
}
|
|
|
|
static void primary_cb(uint8_t status, GSList *services, void *user_data)
|
|
{
|
|
struct hal_ev_gatt_client_search_complete ev;
|
|
struct gatt_device *dev = user_data;
|
|
GSList *l;
|
|
|
|
DBG("Status %d", status);
|
|
|
|
if (status) {
|
|
error("gatt: Discover all primary services failed: %s",
|
|
att_ecode2str(status));
|
|
ev.status = HAL_STATUS_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
if (!services) {
|
|
info("gatt: No primary services found");
|
|
ev.status = HAL_STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
|
|
for (l = services; l; l = l->next) {
|
|
struct hal_ev_gatt_client_search_result ev_res;
|
|
struct gatt_primary *prim = l->data;
|
|
struct gatt_primary *p;
|
|
bt_uuid_t uuid;
|
|
|
|
p = new0(struct gatt_primary, 1);
|
|
if (!p) {
|
|
error("gatt: Cannot allocate memory for gatt_primary");
|
|
continue;
|
|
}
|
|
|
|
memset(&ev_res, 0, sizeof(ev_res));
|
|
|
|
/* Put primary service to our local list */
|
|
memcpy(p, prim, sizeof(*p));
|
|
if (!queue_push_tail(dev->services, p)) {
|
|
error("gatt: Cannot push primary service to the list");
|
|
free(p);
|
|
continue;
|
|
}
|
|
|
|
DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
|
|
prim->range.start, prim->range.end, prim->uuid);
|
|
|
|
/* Set event data */
|
|
ev_res.conn_id = dev->conn_id;
|
|
ev_res.srvc_id.is_primary = 1;
|
|
ev_res.srvc_id.inst_id = 0;
|
|
|
|
if (bt_string_to_uuid(&uuid, prim->uuid) < 0) {
|
|
error("gatt: Cannot convert string to uuid");
|
|
continue;
|
|
}
|
|
|
|
memcpy(&ev_res.srvc_id.uuid, &uuid.value, sizeof(uuid.value));
|
|
|
|
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT ,
|
|
HAL_EV_GATT_CLIENT_SEARCH_RESULT,
|
|
sizeof(ev_res), &ev_res);
|
|
}
|
|
|
|
ev.status = HAL_STATUS_SUCCESS;
|
|
|
|
done:
|
|
ev.conn_id = dev->conn_id;
|
|
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev);
|
|
}
|
|
|
|
static void connection_cleanup(struct gatt_device *device)
|
|
{
|
|
if (device->watch_id) {
|
|
g_source_remove(device->watch_id);
|
|
device->watch_id = 0;
|
|
}
|
|
|
|
if (device->att_io) {
|
|
g_io_channel_shutdown(device->att_io, FALSE, NULL);
|
|
g_io_channel_unref(device->att_io);
|
|
device->att_io = NULL;
|
|
}
|
|
|
|
if (device->attrib) {
|
|
GAttrib *attrib = device->attrib;
|
|
device->attrib = NULL;
|
|
g_attrib_cancel_all(attrib);
|
|
g_attrib_unref(attrib);
|
|
}
|
|
}
|
|
|
|
static void send_client_disconnect_notify(int32_t id, struct gatt_device *dev,
|
|
uint8_t status)
|
|
{
|
|
struct hal_ev_gatt_client_disconnect ev;
|
|
|
|
ev.client_if = id;
|
|
ev.conn_id = dev->conn_id;
|
|
ev.status = status;
|
|
bdaddr2android(&dev->bdaddr, &ev.bda);
|
|
|
|
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
|
|
}
|
|
|
|
static void client_disconnect_notify(void *data, void *user_data)
|
|
{
|
|
struct gatt_device *dev = user_data;
|
|
int32_t id = PTR_TO_INT(data);
|
|
|
|
send_client_disconnect_notify(id, dev, HAL_STATUS_SUCCESS);
|
|
}
|
|
|
|
static bool is_device_wating_for_connect(const bdaddr_t *addr,
|
|
uint8_t addr_type)
|
|
{
|
|
struct gatt_device *dev;
|
|
|
|
DBG("");
|
|
|
|
dev = queue_find(conn_wait_queue, match_dev_by_bdaddr, (void *)addr);
|
|
if (!dev)
|
|
return false;
|
|
|
|
dev->bdaddr_type = addr_type;
|
|
|
|
/* Mark that this device is ready for connect.
|
|
* Need it because will continue with connect after scan is stopped
|
|
*/
|
|
dev->connect_ready = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void le_device_found_handler(const bdaddr_t *addr, uint8_t addr_type,
|
|
int rssi, uint16_t eir_len,
|
|
const void *eir)
|
|
{
|
|
uint8_t buf[IPC_MTU];
|
|
struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
|
|
char bda[18];
|
|
|
|
if (!scanning)
|
|
goto connect;
|
|
|
|
ba2str(addr, bda);
|
|
DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi, !!eir);
|
|
|
|
bdaddr2android(addr, ev->bda);
|
|
ev->rssi = rssi;
|
|
ev->len = eir_len;
|
|
|
|
memcpy(ev->adv_data, eir, ev->len);
|
|
|
|
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_EV_GATT_CLIENT_SCAN_RESULT,
|
|
sizeof(ev) + ev->len, ev);
|
|
|
|
connect:
|
|
if (!is_device_wating_for_connect(addr, addr_type))
|
|
return;
|
|
|
|
/* We are ok to perform connect now. Stop discovery
|
|
* and once it is stopped continue with creating ACL
|
|
*/
|
|
bt_le_discovery_stop(bt_le_discovery_stop_cb);
|
|
}
|
|
|
|
static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
|
|
gpointer user_data)
|
|
{
|
|
bdaddr_t *addr = user_data;
|
|
struct gatt_device *dev;
|
|
int sock, err = 0;
|
|
socklen_t len;
|
|
|
|
dev = queue_remove_if(conn_list, match_dev_by_bdaddr, addr);
|
|
|
|
sock = g_io_channel_unix_get_fd(io);
|
|
len = sizeof(err);
|
|
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
|
|
goto done;
|
|
|
|
DBG("%s (%d)", strerror(err), err);
|
|
|
|
/* Keep scanning/re-connection active if disconnection reason
|
|
* is connection timeout, remote user terminated connection or local
|
|
* initiated disconnection.
|
|
*/
|
|
if (err == ETIMEDOUT || err == ECONNRESET || err == ECONNABORTED) {
|
|
if (!queue_push_tail(conn_wait_queue, dev)) {
|
|
error("gatt: Cannot push data");
|
|
} else {
|
|
bt_le_discovery_start(le_device_found_handler);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
done:
|
|
connection_cleanup(dev);
|
|
|
|
queue_foreach(dev->clients, client_disconnect_notify, dev);
|
|
destroy_device(dev);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void send_client_connect_notify(void *data, void *user_data)
|
|
{
|
|
struct hal_ev_gatt_client_connect *ev = user_data;
|
|
int32_t id = PTR_TO_INT(data);
|
|
|
|
ev->client_if = id;
|
|
|
|
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_EV_GATT_CLIENT_CONNECT, sizeof(*ev), ev);
|
|
|
|
}
|
|
|
|
static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
|
|
{
|
|
bdaddr_t *addr = user_data;
|
|
struct gatt_device *dev;
|
|
struct hal_ev_gatt_client_connect ev;
|
|
GAttrib *attrib;
|
|
static uint32_t conn_id = 0;
|
|
uint8_t status;
|
|
|
|
/* Take device from conn waiting queue */
|
|
dev = queue_remove_if(conn_wait_queue, match_dev_by_bdaddr, addr);
|
|
if (!dev) {
|
|
error("gatt: Device not on the connect wait queue!?");
|
|
g_io_channel_shutdown(io, TRUE, NULL);
|
|
return;
|
|
}
|
|
|
|
g_io_channel_unref(dev->att_io);
|
|
dev->att_io = NULL;
|
|
|
|
/* Set address and client id in the event */
|
|
bdaddr2android(&dev->bdaddr, &ev.bda);
|
|
|
|
if (gerr) {
|
|
error("gatt: connection failed %s", gerr->message);
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
attrib = g_attrib_new(io);
|
|
if (!attrib) {
|
|
error("gatt: unable to create new GAttrib instance");
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
dev->attrib = attrib;
|
|
dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
|
|
disconnected_cb, dev);
|
|
dev->conn_id = ++conn_id;
|
|
|
|
/* Move gatt device from connect queue to conn_list */
|
|
if (!queue_push_tail(conn_list, dev)) {
|
|
error("gatt: Cannot push dev on conn_list");
|
|
connection_cleanup(dev);
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
status = HAL_STATUS_SUCCESS;
|
|
goto reply;
|
|
|
|
reply:
|
|
ev.conn_id = dev ? dev->conn_id : 0;
|
|
ev.status = status;
|
|
|
|
queue_foreach(dev->clients, send_client_connect_notify, &ev);
|
|
|
|
/* If connection did not succeed, destroy device */
|
|
if (status)
|
|
destroy_device(dev);
|
|
|
|
/* Check if we should restart scan */
|
|
if (scanning)
|
|
bt_le_discovery_start(le_device_found_handler);
|
|
|
|
/*FIXME: What to do if discovery won't start here. */
|
|
}
|
|
|
|
static int connect_le(struct gatt_device *dev)
|
|
{
|
|
BtIOSecLevel sec_level;
|
|
GIOChannel *io;
|
|
GError *gerr = NULL;
|
|
char addr[18];
|
|
|
|
ba2str(&dev->bdaddr, addr);
|
|
|
|
/* There is one connection attempt going on */
|
|
if (dev->att_io) {
|
|
info("gatt: connection to dev %s is ongoing", addr);
|
|
return -EALREADY;
|
|
}
|
|
|
|
DBG("Connection attempt to: %s", addr);
|
|
|
|
/*TODO: If we are bonded then we should use higier sec level */
|
|
sec_level = BT_IO_SEC_LOW;
|
|
|
|
/*
|
|
* This connection will help us catch any PDUs that comes before
|
|
* pairing finishes
|
|
*/
|
|
io = bt_io_connect(connect_cb, dev, NULL, &gerr,
|
|
BT_IO_OPT_SOURCE_BDADDR,
|
|
&adapter_addr,
|
|
BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
|
|
BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
|
|
BT_IO_OPT_DEST_TYPE, dev->bdaddr_type,
|
|
BT_IO_OPT_CID, ATT_CID,
|
|
BT_IO_OPT_SEC_LEVEL, sec_level,
|
|
BT_IO_OPT_INVALID);
|
|
if (!io) {
|
|
error("gatt: Failed bt_io_connect(%s): %s", addr,
|
|
gerr->message);
|
|
g_error_free(gerr);
|
|
return -EIO;
|
|
}
|
|
|
|
/* Keep this, so we can cancel the connection */
|
|
dev->att_io = io;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void handle_client_scan(const void *buf, uint16_t len)
|
|
{
|
|
const struct hal_cmd_gatt_client_scan *cmd = buf;
|
|
uint8_t status;
|
|
void *registered;
|
|
|
|
DBG("new state %d", cmd->start);
|
|
|
|
registered = queue_find(gatt_clients, match_client_by_id,
|
|
INT_TO_PTR(cmd->client_if));
|
|
if (!registered) {
|
|
error("gatt: Client not registered");
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
/* Turn off scan */
|
|
if (!cmd->start) {
|
|
DBG("Stopping LE SCAN");
|
|
|
|
if (scanning) {
|
|
bt_le_discovery_stop(NULL);
|
|
scanning = false;
|
|
}
|
|
|
|
status = HAL_STATUS_SUCCESS;
|
|
goto reply;
|
|
}
|
|
|
|
/* Reply success if we already do scan */
|
|
if (scanning) {
|
|
status = HAL_STATUS_SUCCESS;
|
|
goto reply;
|
|
}
|
|
|
|
/* Turn on scan */
|
|
if (!bt_le_discovery_start(le_device_found_handler)) {
|
|
error("gatt: LE scan switch failed");
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
scanning = true;
|
|
status = HAL_STATUS_SUCCESS;
|
|
|
|
reply:
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN,
|
|
status);
|
|
}
|
|
|
|
static int connect_next_dev(void)
|
|
{
|
|
struct gatt_device *dev;
|
|
|
|
DBG("");
|
|
|
|
if (queue_isempty(conn_wait_queue))
|
|
return 0;
|
|
|
|
/* Discovery has been stopped because there is connection waiting */
|
|
dev = queue_find(conn_wait_queue, match_dev_connect_ready, NULL);
|
|
if (!dev)
|
|
/* Lets try again. */
|
|
return -1;
|
|
|
|
dev->connect_ready = false;
|
|
|
|
return connect_le(dev);
|
|
}
|
|
|
|
static void bt_le_discovery_stop_cb(void)
|
|
{
|
|
DBG("");
|
|
|
|
/* Check now if there is any device ready to connect*/
|
|
if (connect_next_dev() < 0)
|
|
bt_le_discovery_start(le_device_found_handler);
|
|
}
|
|
|
|
static struct gatt_device *find_device(bdaddr_t *addr)
|
|
{
|
|
struct gatt_device *dev;
|
|
|
|
dev = queue_find(conn_list, match_dev_by_bdaddr, addr);
|
|
if (dev)
|
|
return dev;
|
|
|
|
dev = queue_find(conn_wait_queue, match_dev_by_bdaddr, addr);
|
|
if (dev)
|
|
return dev;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void handle_client_connect(const void *buf, uint16_t len)
|
|
{
|
|
const struct hal_cmd_gatt_client_connect *cmd = buf;
|
|
struct gatt_device *dev = NULL;
|
|
void *l;
|
|
bdaddr_t addr;
|
|
uint8_t status;
|
|
bool send_notify = false;
|
|
|
|
DBG("");
|
|
|
|
/* Check if client is registered */
|
|
l = queue_find(gatt_clients, match_client_by_id,
|
|
INT_TO_PTR(cmd->client_if));
|
|
if (!l) {
|
|
error("gatt: Client id %d not found", cmd->client_if);
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
android2bdaddr(&cmd->bdaddr, &addr);
|
|
|
|
/* We do support many clients for one device connection so lets check
|
|
* If device is connected or in connecting state just update list of
|
|
* clients
|
|
*/
|
|
dev = find_device(&addr);
|
|
if (dev) {
|
|
/* Remeber to send dummy notification event if we area
|
|
* connected
|
|
*/
|
|
if (dev->conn_id)
|
|
send_notify = true;
|
|
|
|
if (queue_find(dev->clients, match_by_value,
|
|
INT_TO_PTR(cmd->client_if))) {
|
|
status = HAL_STATUS_SUCCESS;
|
|
goto reply;
|
|
}
|
|
|
|
/* Store another client */
|
|
if (!queue_push_tail(dev->clients,
|
|
INT_TO_PTR(cmd->client_if))) {
|
|
error("gatt: Cannot push client on gatt device list");
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
status = HAL_STATUS_SUCCESS;
|
|
goto reply;
|
|
}
|
|
|
|
/* Lets create new gatt device and put it on conn_wait_queue.
|
|
* Once it is connected we move it to conn_list
|
|
*/
|
|
dev = new0(struct gatt_device, 1);
|
|
if (!dev) {
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
memcpy(&dev->bdaddr, &addr, sizeof(bdaddr_t));
|
|
|
|
/* Create queue to keep list of clients for given device*/
|
|
dev->clients = queue_new();
|
|
if (!dev->clients) {
|
|
error("gatt: Cannot create client queue");
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
dev->services = queue_new();
|
|
if (!dev->services) {
|
|
error("gatt: Cannot create services queue");
|
|
queue_destroy(dev->clients, NULL);
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
/* Update client list of device */
|
|
if (!queue_push_tail(dev->clients, INT_TO_PTR(cmd->client_if))) {
|
|
error("gatt: Cannot push client on the client queue!?");
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
/* Start le scan if not started */
|
|
if (!scanning && !bt_le_discovery_start(le_device_found_handler)) {
|
|
error("gatt: Could not start scan");
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
if (!queue_push_tail(conn_wait_queue, dev)) {
|
|
error("gatt: Cannot push device on conn_wait_queue");
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
status = HAL_STATUS_SUCCESS;
|
|
|
|
reply:
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
|
|
status);
|
|
|
|
/* If there is an error here we should make sure dev is out.*/
|
|
if ((status != HAL_STATUS_SUCCESS) && dev) {
|
|
destroy_device(dev);
|
|
return;
|
|
}
|
|
|
|
/* Send dummy notification since ACL is already up*/
|
|
if (send_notify) {
|
|
struct hal_ev_gatt_client_connect ev;
|
|
|
|
ev.conn_id = dev->conn_id;
|
|
ev.status = HAL_STATUS_SUCCESS;
|
|
ev.client_if = cmd->client_if;
|
|
bdaddr2android(&addr, &ev.bda);
|
|
|
|
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_EV_GATT_CLIENT_CONNECT,
|
|
sizeof(ev), &ev);
|
|
}
|
|
}
|
|
|
|
static void handle_client_disconnect(const void *buf, uint16_t len)
|
|
{
|
|
const struct hal_cmd_gatt_client_disconnect *cmd = buf;
|
|
struct gatt_device *dev;
|
|
uint8_t status;
|
|
char addr[18];
|
|
|
|
DBG("");
|
|
|
|
ba2str((bdaddr_t *)&cmd->bdaddr, addr);
|
|
|
|
dev = queue_find(conn_list, match_dev_by_conn_id,
|
|
INT_TO_PTR(cmd->conn_id));
|
|
if (!dev) {
|
|
error("gatt: dev %s with conn_id=%d not found",
|
|
addr, cmd->conn_id);
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
/*Check if client owns this connection */
|
|
if (!queue_remove_if(dev->clients, match_by_value,
|
|
INT_TO_PTR(cmd->client_if))) {
|
|
error("gatt: cannot remove conn_id=%d", cmd->client_if);
|
|
status = HAL_STATUS_FAILED;
|
|
} else {
|
|
status = HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
reply:
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_DISCONNECT, status);
|
|
|
|
if (status == HAL_STATUS_FAILED)
|
|
return;
|
|
|
|
/* Just send disconnect event. If there is more clients on this
|
|
* device then this is what we shall to do.
|
|
* If this is last client, this is still OK to do because on connect
|
|
* request we do le scan and wait until remote device start
|
|
* advertisement */
|
|
send_client_disconnect_notify(cmd->client_if, dev, HAL_STATUS_SUCCESS);
|
|
|
|
/* If there is more clients just return */
|
|
if (!queue_isempty(dev->clients))
|
|
return;
|
|
|
|
/* If this is last client do more cleaning */
|
|
connection_cleanup(dev);
|
|
dev = queue_remove_if(conn_list, match_dev_by_bdaddr, &dev->bdaddr);
|
|
destroy_device(dev);
|
|
}
|
|
|
|
static void handle_client_listen(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN,
|
|
HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_refresh(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH,
|
|
HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_search_service(const void *buf, uint16_t len)
|
|
{
|
|
const struct hal_cmd_gatt_client_search_service *cmd = buf;
|
|
struct gatt_device *dev;
|
|
uint8_t status;
|
|
|
|
DBG("");
|
|
|
|
dev = queue_find(conn_list, match_dev_by_conn_id,
|
|
INT_TO_PTR(cmd->conn_id));
|
|
if (!dev) {
|
|
error("gatt: dev with conn_id=%d not found", cmd->conn_id);
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
/*TODO: Handle filter uuid */
|
|
|
|
if (!gatt_discover_primary(dev->attrib, NULL, primary_cb, dev)) {
|
|
status = HAL_STATUS_FAILED;
|
|
goto reply;
|
|
}
|
|
|
|
status = HAL_STATUS_SUCCESS;
|
|
|
|
reply:
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_SEARCH_SERVICE, status);
|
|
}
|
|
|
|
static void handle_client_get_included_service(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE,
|
|
HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_get_characteristic(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC,
|
|
HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_get_descriptor(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_read_characteristic(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC,
|
|
HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_write_characteristic(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC,
|
|
HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_read_descriptor(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_write_descriptor(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_execute_write(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_EXECUTE_WRITE, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_register_for_notification(const void *buf,
|
|
uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION,
|
|
HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_deregister_for_notification(const void *buf,
|
|
uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION,
|
|
HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_read_remote_rssi(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_get_device_type(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_set_adv_data(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_SET_ADV_DATA, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_client_test_command(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_CLIENT_TEST_COMMAND, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_server_register(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER,
|
|
HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_server_unregister(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_SERVER_UNREGISTER, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_server_connect(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT,
|
|
HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_server_disconnect(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_SERVER_DISCONNECT, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_server_add_service(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_SERVER_ADD_SERVICE, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_server_add_included_service(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_SERVER_ADD_INC_SERVICE, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_server_add_characteristic(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC,
|
|
HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_server_add_descriptor(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_server_start_service(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_SERVER_START_SERVICE, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_server_stop_service(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_SERVER_STOP_SERVICE, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_server_delete_service(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_SERVER_DELETE_SERVICE, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_server_send_indication(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_SERVER_SEND_INDICATION, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static void handle_server_send_response(const void *buf, uint16_t len)
|
|
{
|
|
DBG("");
|
|
|
|
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
|
|
HAL_OP_GATT_SERVER_SEND_RESPONSE, HAL_STATUS_FAILED);
|
|
}
|
|
|
|
static const struct ipc_handler cmd_handlers[] = {
|
|
/* HAL_OP_GATT_CLIENT_REGISTER */
|
|
{handle_client_register, false,
|
|
sizeof(struct hal_cmd_gatt_client_register)},
|
|
/* HAL_OP_GATT_CLIENT_UNREGISTER */
|
|
{handle_client_unregister, false,
|
|
sizeof(struct hal_cmd_gatt_client_unregister)},
|
|
/* HAL_OP_GATT_CLIENT_SCAN */
|
|
{handle_client_scan, false,
|
|
sizeof(struct hal_cmd_gatt_client_scan)},
|
|
/* HAL_OP_GATT_CLIENT_CONNECT */
|
|
{handle_client_connect, false,
|
|
sizeof(struct hal_cmd_gatt_client_connect)},
|
|
/* HAL_OP_GATT_CLIENT_DISCONNECT */
|
|
{handle_client_disconnect, false,
|
|
sizeof(struct hal_cmd_gatt_client_disconnect)},
|
|
/* HAL_OP_GATT_CLIENT_LISTEN */
|
|
{handle_client_listen, false,
|
|
sizeof(struct hal_cmd_gatt_client_listen)},
|
|
/* HAL_OP_GATT_CLIENT_REFRESH */
|
|
{handle_client_refresh, false,
|
|
sizeof(struct hal_cmd_gatt_client_refresh)},
|
|
/* HAL_OP_GATT_CLIENT_SEARCH_SERVICE */
|
|
{handle_client_search_service, true,
|
|
sizeof(struct hal_cmd_gatt_client_search_service)},
|
|
/* HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE */
|
|
{handle_client_get_included_service, true,
|
|
sizeof(struct hal_cmd_gatt_client_get_included_service)},
|
|
/* HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC */
|
|
{handle_client_get_characteristic, true,
|
|
sizeof(struct hal_cmd_gatt_client_get_characteristic)},
|
|
/* HAL_OP_GATT_CLIENT_GET_DESCRIPTOR */
|
|
{handle_client_get_descriptor, true,
|
|
sizeof(struct hal_cmd_gatt_client_get_descriptor)},
|
|
/* HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC */
|
|
{handle_client_read_characteristic, false,
|
|
sizeof(struct hal_cmd_gatt_client_read_characteristic)},
|
|
/* HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC */
|
|
{handle_client_write_characteristic, true,
|
|
sizeof(struct hal_cmd_gatt_client_write_characteristic)},
|
|
/* HAL_OP_GATT_CLIENT_READ_DESCRIPTOR */
|
|
{handle_client_read_descriptor, false,
|
|
sizeof(struct hal_cmd_gatt_client_read_descriptor)},
|
|
/* HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR */
|
|
{handle_client_write_descriptor, true,
|
|
sizeof(struct hal_cmd_gatt_client_write_descriptor)},
|
|
/* HAL_OP_GATT_CLIENT_EXECUTE_WRITE */
|
|
{handle_client_execute_write, false,
|
|
sizeof(struct hal_cmd_gatt_client_execute_write)},
|
|
/* HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION */
|
|
{handle_client_register_for_notification, false,
|
|
sizeof(struct hal_cmd_gatt_client_register_for_notification)},
|
|
/* HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION */
|
|
{handle_client_deregister_for_notification, false,
|
|
sizeof(struct hal_cmd_gatt_client_deregister_for_notification)},
|
|
/* HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI */
|
|
{handle_client_read_remote_rssi, false,
|
|
sizeof(struct hal_cmd_gatt_client_read_remote_rssi)},
|
|
/* HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE */
|
|
{handle_client_get_device_type, false,
|
|
sizeof(struct hal_cmd_gatt_client_get_device_type)},
|
|
/* HAL_OP_GATT_CLIENT_SET_ADV_DATA */
|
|
{handle_client_set_adv_data, true,
|
|
sizeof(struct hal_cmd_gatt_client_set_adv_data)},
|
|
/* HAL_OP_GATT_CLIENT_TEST_COMMAND */
|
|
{handle_client_test_command, false,
|
|
sizeof(struct hal_cmd_gatt_client_test_command)},
|
|
/* HAL_OP_GATT_SERVER_REGISTER */
|
|
{handle_server_register, false,
|
|
sizeof(struct hal_cmd_gatt_server_register)},
|
|
/* HAL_OP_GATT_SERVER_UNREGISTER */
|
|
{handle_server_unregister, false,
|
|
sizeof(struct hal_cmd_gatt_server_unregister)},
|
|
/* HAL_OP_GATT_SERVER_CONNECT */
|
|
{handle_server_connect, false,
|
|
sizeof(struct hal_cmd_gatt_server_connect)},
|
|
/* HAL_OP_GATT_SERVER_DISCONNECT */
|
|
{handle_server_disconnect, false,
|
|
sizeof(struct hal_cmd_gatt_server_disconnect)},
|
|
/* HAL_OP_GATT_SERVER_ADD_SERVICE */
|
|
{handle_server_add_service, false,
|
|
sizeof(struct hal_cmd_gatt_server_add_service)},
|
|
/* HAL_OP_GATT_SERVER_ADD_INC_SERVICE */
|
|
{handle_server_add_included_service, false,
|
|
sizeof(struct hal_cmd_gatt_server_add_inc_service)},
|
|
/* HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC */
|
|
{handle_server_add_characteristic, false,
|
|
sizeof(struct hal_cmd_gatt_server_add_characteristic)},
|
|
/* HAL_OP_GATT_SERVER_ADD_DESCRIPTOR */
|
|
{handle_server_add_descriptor, false,
|
|
sizeof(struct hal_cmd_gatt_server_add_descriptor)},
|
|
/* HAL_OP_GATT_SERVER_START_SERVICE */
|
|
{handle_server_start_service, false,
|
|
sizeof(struct hal_cmd_gatt_server_start_service)},
|
|
/* HAL_OP_GATT_SERVER_STOP_SERVICE */
|
|
{handle_server_stop_service, false,
|
|
sizeof(struct hal_cmd_gatt_server_stop_service)},
|
|
/* HAL_OP_GATT_SERVER_DELETE_SERVICE */
|
|
{handle_server_delete_service, false,
|
|
sizeof(struct hal_cmd_gatt_server_delete_service)},
|
|
/* HAL_OP_GATT_SERVER_SEND_INDICATION */
|
|
{handle_server_send_indication, true,
|
|
sizeof(struct hal_cmd_gatt_server_send_indication)},
|
|
/* HAL_OP_GATT_SERVER_SEND_RESPONSE */
|
|
{handle_server_send_response, true,
|
|
sizeof(struct hal_cmd_gatt_server_send_response)},
|
|
};
|
|
|
|
bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
|
|
{
|
|
DBG("");
|
|
|
|
bacpy(&adapter_addr, addr);
|
|
|
|
hal_ipc = ipc;
|
|
|
|
conn_list = queue_new();
|
|
if (!conn_list) {
|
|
error("gatt: Can not create conn queue");
|
|
return false;
|
|
}
|
|
|
|
conn_wait_queue = queue_new();
|
|
if (!conn_wait_queue) {
|
|
error("gatt: Can not create conn queue");
|
|
return false;
|
|
}
|
|
|
|
ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers,
|
|
G_N_ELEMENTS(cmd_handlers));
|
|
|
|
gatt_clients = queue_new();
|
|
if (!gatt_clients) {
|
|
error("gatt: Cannot allocate gatt_clients");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void bt_gatt_unregister(void)
|
|
{
|
|
DBG("");
|
|
|
|
queue_destroy(gatt_clients, free);
|
|
|
|
ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT);
|
|
hal_ipc = NULL;
|
|
|
|
queue_destroy(conn_list, destroy_device);
|
|
conn_list = NULL;
|
|
|
|
queue_destroy(conn_wait_queue, destroy_device);
|
|
conn_wait_queue = NULL;
|
|
|
|
}
|