| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- // SPDX-License-Identifier: LGPL-2.1-or-later
- /*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2014 Intel Corporation. All rights reserved.
- *
- *
- */
- #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 "profiles/battery/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);
- free(bas);
- }
- struct bt_bas *bt_bas_new(void *primary)
- {
- struct bt_bas *bas;
- bas = new0(struct bt_bas, 1);
- bas->gatt_op = queue_new();
- 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);
- req->user_data = user_data;
- req->bas = bt_bas_ref(bas);
- return req;
- }
- static void set_and_store_gatt_req(struct bt_bas *bas,
- struct gatt_request *req,
- unsigned int id)
- {
- req->id = id;
- 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);
- id = gatt_write_char(attrib, handle, value, vlen, func, req);
- set_and_store_gatt_req(bas, req, id);
- }
- 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);
- id = gatt_read_char(attrib, handle, func, req);
- set_and_store_gatt_req(bas, req, id);
- }
- 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);
- id = gatt_discover_char(attrib, start, end, uuid, func, req);
- set_and_store_gatt_req(bas, req, id);
- }
- 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);
- id = gatt_discover_desc(attrib, start, end, uuid, func, req);
- set_and_store_gatt_req(bas, req, id);
- }
- 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;
- }
|