bas.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. // SPDX-License-Identifier: LGPL-2.1-or-later
  2. /*
  3. *
  4. * BlueZ - Bluetooth protocol stack for Linux
  5. *
  6. * Copyright (C) 2014 Intel Corporation. All rights reserved.
  7. *
  8. *
  9. */
  10. #ifdef HAVE_CONFIG_H
  11. #include <config.h>
  12. #endif
  13. #include <stdbool.h>
  14. #include <errno.h>
  15. #include <glib.h>
  16. #include "src/log.h"
  17. #include "lib/bluetooth.h"
  18. #include "lib/sdp.h"
  19. #include "lib/uuid.h"
  20. #include "src/shared/util.h"
  21. #include "src/shared/queue.h"
  22. #include "attrib/gattrib.h"
  23. #include "attrib/att.h"
  24. #include "attrib/gatt.h"
  25. #include "profiles/battery/bas.h"
  26. #define ATT_NOTIFICATION_HEADER_SIZE 3
  27. #define ATT_READ_RESPONSE_HEADER_SIZE 1
  28. struct bt_bas {
  29. int ref_count;
  30. GAttrib *attrib;
  31. struct gatt_primary *primary;
  32. uint16_t handle;
  33. uint16_t ccc_handle;
  34. guint id;
  35. struct queue *gatt_op;
  36. };
  37. struct gatt_request {
  38. unsigned int id;
  39. struct bt_bas *bas;
  40. void *user_data;
  41. };
  42. static void destroy_gatt_req(struct gatt_request *req)
  43. {
  44. queue_remove(req->bas->gatt_op, req);
  45. bt_bas_unref(req->bas);
  46. free(req);
  47. }
  48. static void bas_free(struct bt_bas *bas)
  49. {
  50. bt_bas_detach(bas);
  51. g_free(bas->primary);
  52. queue_destroy(bas->gatt_op, (void *) destroy_gatt_req);
  53. free(bas);
  54. }
  55. struct bt_bas *bt_bas_new(void *primary)
  56. {
  57. struct bt_bas *bas;
  58. bas = new0(struct bt_bas, 1);
  59. bas->gatt_op = queue_new();
  60. if (primary)
  61. bas->primary = g_memdup(primary, sizeof(*bas->primary));
  62. return bt_bas_ref(bas);
  63. }
  64. struct bt_bas *bt_bas_ref(struct bt_bas *bas)
  65. {
  66. if (!bas)
  67. return NULL;
  68. __sync_fetch_and_add(&bas->ref_count, 1);
  69. return bas;
  70. }
  71. void bt_bas_unref(struct bt_bas *bas)
  72. {
  73. if (!bas)
  74. return;
  75. if (__sync_sub_and_fetch(&bas->ref_count, 1))
  76. return;
  77. bas_free(bas);
  78. }
  79. static struct gatt_request *create_request(struct bt_bas *bas,
  80. void *user_data)
  81. {
  82. struct gatt_request *req;
  83. req = new0(struct gatt_request, 1);
  84. req->user_data = user_data;
  85. req->bas = bt_bas_ref(bas);
  86. return req;
  87. }
  88. static void set_and_store_gatt_req(struct bt_bas *bas,
  89. struct gatt_request *req,
  90. unsigned int id)
  91. {
  92. req->id = id;
  93. queue_push_head(bas->gatt_op, req);
  94. }
  95. static void write_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
  96. const uint8_t *value, size_t vlen,
  97. GAttribResultFunc func,
  98. gpointer user_data)
  99. {
  100. struct gatt_request *req;
  101. unsigned int id;
  102. req = create_request(bas, user_data);
  103. id = gatt_write_char(attrib, handle, value, vlen, func, req);
  104. set_and_store_gatt_req(bas, req, id);
  105. }
  106. static void read_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
  107. GAttribResultFunc func, gpointer user_data)
  108. {
  109. struct gatt_request *req;
  110. unsigned int id;
  111. req = create_request(bas, user_data);
  112. id = gatt_read_char(attrib, handle, func, req);
  113. set_and_store_gatt_req(bas, req, id);
  114. }
  115. static void discover_char(struct bt_bas *bas, GAttrib *attrib,
  116. uint16_t start, uint16_t end,
  117. bt_uuid_t *uuid, gatt_cb_t func,
  118. gpointer user_data)
  119. {
  120. struct gatt_request *req;
  121. unsigned int id;
  122. req = create_request(bas, user_data);
  123. id = gatt_discover_char(attrib, start, end, uuid, func, req);
  124. set_and_store_gatt_req(bas, req, id);
  125. }
  126. static void discover_desc(struct bt_bas *bas, GAttrib *attrib,
  127. uint16_t start, uint16_t end, bt_uuid_t *uuid,
  128. gatt_cb_t func, gpointer user_data)
  129. {
  130. struct gatt_request *req;
  131. unsigned int id;
  132. req = create_request(bas, user_data);
  133. id = gatt_discover_desc(attrib, start, end, uuid, func, req);
  134. set_and_store_gatt_req(bas, req, id);
  135. }
  136. static void notification_cb(const guint8 *pdu, guint16 len, gpointer user_data)
  137. {
  138. DBG("Battery Level at %u", pdu[ATT_NOTIFICATION_HEADER_SIZE]);
  139. }
  140. static void read_value_cb(guint8 status, const guint8 *pdu, guint16 len,
  141. gpointer user_data)
  142. {
  143. DBG("Battery Level at %u", pdu[ATT_READ_RESPONSE_HEADER_SIZE]);
  144. }
  145. static void ccc_written_cb(guint8 status, const guint8 *pdu,
  146. guint16 plen, gpointer user_data)
  147. {
  148. struct gatt_request *req = user_data;
  149. struct bt_bas *bas = req->user_data;
  150. destroy_gatt_req(req);
  151. if (status != 0) {
  152. error("Write Scan Refresh CCC failed: %s",
  153. att_ecode2str(status));
  154. return;
  155. }
  156. DBG("Battery Level: notification enabled");
  157. bas->id = g_attrib_register(bas->attrib, ATT_OP_HANDLE_NOTIFY,
  158. bas->handle, notification_cb, bas,
  159. NULL);
  160. }
  161. static void write_ccc(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
  162. void *user_data)
  163. {
  164. uint8_t value[2];
  165. put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
  166. write_char(bas, attrib, handle, value, sizeof(value), ccc_written_cb,
  167. user_data);
  168. }
  169. static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
  170. gpointer user_data)
  171. {
  172. struct gatt_request *req = user_data;
  173. struct bt_bas *bas = req->user_data;
  174. destroy_gatt_req(req);
  175. if (status != 0) {
  176. error("Error reading CCC value: %s", att_ecode2str(status));
  177. return;
  178. }
  179. write_ccc(bas, bas->attrib, bas->ccc_handle, bas);
  180. }
  181. static void discover_descriptor_cb(uint8_t status, GSList *descs,
  182. void *user_data)
  183. {
  184. struct gatt_request *req = user_data;
  185. struct bt_bas *bas = req->user_data;
  186. struct gatt_desc *desc;
  187. destroy_gatt_req(req);
  188. if (status != 0) {
  189. error("Discover descriptors failed: %s", att_ecode2str(status));
  190. return;
  191. }
  192. /* There will be only one descriptor on list and it will be CCC */
  193. desc = descs->data;
  194. bas->ccc_handle = desc->handle;
  195. read_char(bas, bas->attrib, desc->handle, ccc_read_cb, bas);
  196. }
  197. static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data)
  198. {
  199. struct gatt_request *req = user_data;
  200. struct bt_bas *bas = req->user_data;
  201. struct gatt_char *chr;
  202. uint16_t start, end;
  203. bt_uuid_t uuid;
  204. destroy_gatt_req(req);
  205. if (status) {
  206. error("Battery: %s", att_ecode2str(status));
  207. return;
  208. }
  209. chr = chars->data;
  210. bas->handle = chr->value_handle;
  211. DBG("Battery handle: 0x%04x", bas->handle);
  212. read_char(bas, bas->attrib, bas->handle, read_value_cb, bas);
  213. start = chr->value_handle + 1;
  214. end = bas->primary->range.end;
  215. bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
  216. discover_desc(bas, bas->attrib, start, end, &uuid,
  217. discover_descriptor_cb, bas);
  218. }
  219. bool bt_bas_attach(struct bt_bas *bas, void *attrib)
  220. {
  221. if (!bas || bas->attrib || !bas->primary)
  222. return false;
  223. bas->attrib = g_attrib_ref(attrib);
  224. if (bas->handle > 0)
  225. return true;
  226. discover_char(bas, bas->attrib, bas->primary->range.start,
  227. bas->primary->range.end, NULL,
  228. bas_discovered_cb, bas);
  229. return true;
  230. }
  231. static void cancel_gatt_req(struct gatt_request *req)
  232. {
  233. if (g_attrib_cancel(req->bas->attrib, req->id))
  234. destroy_gatt_req(req);
  235. }
  236. void bt_bas_detach(struct bt_bas *bas)
  237. {
  238. if (!bas || !bas->attrib)
  239. return;
  240. if (bas->id > 0) {
  241. g_attrib_unregister(bas->attrib, bas->id);
  242. bas->id = 0;
  243. }
  244. queue_foreach(bas->gatt_op, (void *) cancel_gatt_req, NULL);
  245. g_attrib_unref(bas->attrib);
  246. bas->attrib = NULL;
  247. }