diff options
Diffstat (limited to 'android/bas.c')
-rwxr-xr-x | android/bas.c | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/android/bas.c b/android/bas.c new file mode 100755 index 00000000..dcbf9de7 --- /dev/null +++ b/android/bas.c @@ -0,0 +1,379 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can rebastribute 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 bastributed 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 <errno.h> + +#include <glib.h> + +#include "src/log.h" + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "src/shared/util.h" +#include "src/shared/queue.h" + +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" + +#include "android/bas.h" + +#define ATT_NOTIFICATION_HEADER_SIZE 3 +#define ATT_READ_RESPONSE_HEADER_SIZE 1 + +struct bt_bas { + int ref_count; + GAttrib *attrib; + struct gatt_primary *primary; + uint16_t handle; + uint16_t ccc_handle; + guint id; + struct queue *gatt_op; +}; + +struct gatt_request { + unsigned int id; + struct bt_bas *bas; + void *user_data; +}; + +static void destroy_gatt_req(struct gatt_request *req) +{ + queue_remove(req->bas->gatt_op, req); + bt_bas_unref(req->bas); + free(req); +} + +static void bas_free(struct bt_bas *bas) +{ + bt_bas_detach(bas); + + g_free(bas->primary); + queue_destroy(bas->gatt_op, (void *) destroy_gatt_req); + g_free(bas); +} + +struct bt_bas *bt_bas_new(void *primary) +{ + struct bt_bas *bas; + + bas = g_try_new0(struct bt_bas, 1); + if (!bas) + return NULL; + + bas->gatt_op = queue_new(); + if (!bas->gatt_op) { + bas_free(bas); + return NULL; + } + + if (primary) + bas->primary = g_memdup(primary, sizeof(*bas->primary)); + + return bt_bas_ref(bas); +} + +struct bt_bas *bt_bas_ref(struct bt_bas *bas) +{ + if (!bas) + return NULL; + + __sync_fetch_and_add(&bas->ref_count, 1); + + return bas; +} + +void bt_bas_unref(struct bt_bas *bas) +{ + if (!bas) + return; + + if (__sync_sub_and_fetch(&bas->ref_count, 1)) + return; + + bas_free(bas); +} + +static struct gatt_request *create_request(struct bt_bas *bas, + void *user_data) +{ + struct gatt_request *req; + + req = new0(struct gatt_request, 1); + if (!req) + return NULL; + + req->user_data = user_data; + req->bas = bt_bas_ref(bas); + + return req; +} + +static bool set_and_store_gatt_req(struct bt_bas *bas, + struct gatt_request *req, + unsigned int id) +{ + req->id = id; + return queue_push_head(bas->gatt_op, req); +} + +static void write_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle, + const uint8_t *value, size_t vlen, + GAttribResultFunc func, + gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(bas, user_data); + if (!req) + return; + + id = gatt_write_char(attrib, handle, value, vlen, func, req); + + if (set_and_store_gatt_req(bas, req, id)) + return; + + error("bas: Could not write characteristic"); + g_attrib_cancel(attrib, id); + free(req); + +} + +static void read_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle, + GAttribResultFunc func, gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(bas, user_data); + if (!req) + return; + + id = gatt_read_char(attrib, handle, func, req); + + if (set_and_store_gatt_req(bas, req, id)) + return; + + error("bas: Could not read characteristic"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void discover_char(struct bt_bas *bas, GAttrib *attrib, + uint16_t start, uint16_t end, + bt_uuid_t *uuid, gatt_cb_t func, + gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(bas, user_data); + if (!req) + return; + + id = gatt_discover_char(attrib, start, end, uuid, func, req); + + if (set_and_store_gatt_req(bas, req, id)) + return; + + error("bas: Could not discover characteristic"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void discover_desc(struct bt_bas *bas, GAttrib *attrib, + uint16_t start, uint16_t end, bt_uuid_t *uuid, + gatt_cb_t func, gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(bas, user_data); + if (!req) + return; + + id = gatt_discover_desc(attrib, start, end, uuid, func, req); + if (set_and_store_gatt_req(bas, req, id)) + return; + + error("bas: Could not discover descriptor"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void notification_cb(const guint8 *pdu, guint16 len, gpointer user_data) +{ + DBG("Battery Level at %u", pdu[ATT_NOTIFICATION_HEADER_SIZE]); +} + +static void read_value_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + DBG("Battery Level at %u", pdu[ATT_READ_RESPONSE_HEADER_SIZE]); +} + +static void ccc_written_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + struct gatt_request *req = user_data; + struct bt_bas *bas = req->user_data; + + destroy_gatt_req(req); + + if (status != 0) { + error("Write Scan Refresh CCC failed: %s", + att_ecode2str(status)); + return; + } + + DBG("Battery Level: notification enabled"); + + bas->id = g_attrib_register(bas->attrib, ATT_OP_HANDLE_NOTIFY, + bas->handle, notification_cb, bas, + NULL); +} + +static void write_ccc(struct bt_bas *bas, GAttrib *attrib, uint16_t handle, + void *user_data) +{ + uint8_t value[2]; + + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + + write_char(bas, attrib, handle, value, sizeof(value), ccc_written_cb, + user_data); +} + +static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct gatt_request *req = user_data; + struct bt_bas *bas = req->user_data; + + destroy_gatt_req(req); + + if (status != 0) { + error("Error reading CCC value: %s", att_ecode2str(status)); + return; + } + + write_ccc(bas, bas->attrib, bas->ccc_handle, bas); +} + +static void discover_descriptor_cb(uint8_t status, GSList *descs, + void *user_data) +{ + struct gatt_request *req = user_data; + struct bt_bas *bas = req->user_data; + struct gatt_desc *desc; + + destroy_gatt_req(req); + + if (status != 0) { + error("Discover descriptors failed: %s", att_ecode2str(status)); + return; + } + + /* There will be only one descriptor on list and it will be CCC */ + desc = descs->data; + bas->ccc_handle = desc->handle; + + read_char(bas, bas->attrib, desc->handle, ccc_read_cb, bas); +} + +static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data) +{ + struct gatt_request *req = user_data; + struct bt_bas *bas = req->user_data; + struct gatt_char *chr; + uint16_t start, end; + bt_uuid_t uuid; + + destroy_gatt_req(req); + + if (status) { + error("Battery: %s", att_ecode2str(status)); + return; + } + + chr = chars->data; + bas->handle = chr->value_handle; + + DBG("Battery handle: 0x%04x", bas->handle); + + read_char(bas, bas->attrib, bas->handle, read_value_cb, bas); + + start = chr->value_handle + 1; + end = bas->primary->range.end; + + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + + discover_desc(bas, bas->attrib, start, end, &uuid, + discover_descriptor_cb, bas); +} + +bool bt_bas_attach(struct bt_bas *bas, void *attrib) +{ + if (!bas || bas->attrib || !bas->primary) + return false; + + bas->attrib = g_attrib_ref(attrib); + + if (bas->handle > 0) + return true; + + discover_char(bas, bas->attrib, bas->primary->range.start, + bas->primary->range.end, NULL, + bas_discovered_cb, bas); + + return true; +} + +static void cancel_gatt_req(struct gatt_request *req) +{ + if (g_attrib_cancel(req->bas->attrib, req->id)) + destroy_gatt_req(req); +} + +void bt_bas_detach(struct bt_bas *bas) +{ + if (!bas || !bas->attrib) + return; + + if (bas->id > 0) { + g_attrib_unregister(bas->attrib, bas->id); + bas->id = 0; + } + + queue_foreach(bas->gatt_op, (void *) cancel_gatt_req, NULL); + g_attrib_unref(bas->attrib); + bas->attrib = NULL; +} |