scpp.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. *
  4. * BlueZ - Bluetooth protocol stack for Linux
  5. *
  6. * Copyright (C) 2012 Nordic Semiconductor Inc.
  7. * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT
  8. *
  9. *
  10. */
  11. #ifdef HAVE_CONFIG_H
  12. #include <config.h>
  13. #endif
  14. #include <stdbool.h>
  15. #include <errno.h>
  16. #include <glib.h>
  17. #include "src/log.h"
  18. #include "lib/bluetooth.h"
  19. #include "lib/sdp.h"
  20. #include "lib/uuid.h"
  21. #include "src/shared/util.h"
  22. #include "src/shared/queue.h"
  23. #include "attrib/att.h"
  24. #include "attrib/gattrib.h"
  25. #include "attrib/gatt.h"
  26. #include "profiles/scanparam/scpp.h"
  27. #define SCAN_INTERVAL_WIN_UUID 0x2A4F
  28. #define SCAN_REFRESH_UUID 0x2A31
  29. #define SCAN_INTERVAL 0x0060
  30. #define SCAN_WINDOW 0x0030
  31. #define SERVER_REQUIRES_REFRESH 0x00
  32. struct bt_scpp {
  33. int ref_count;
  34. GAttrib *attrib;
  35. struct gatt_primary *primary;
  36. uint16_t interval;
  37. uint16_t window;
  38. uint16_t iwhandle;
  39. uint16_t refresh_handle;
  40. guint refresh_cb_id;
  41. struct queue *gatt_op;
  42. };
  43. static void discover_char(struct bt_scpp *scpp, GAttrib *attrib,
  44. uint16_t start, uint16_t end,
  45. bt_uuid_t *uuid, gatt_cb_t func,
  46. gpointer user_data)
  47. {
  48. unsigned int id;
  49. id = gatt_discover_char(attrib, start, end, uuid, func, user_data);
  50. queue_push_head(scpp->gatt_op, UINT_TO_PTR(id));
  51. }
  52. static void discover_desc(struct bt_scpp *scpp, GAttrib *attrib,
  53. uint16_t start, uint16_t end, bt_uuid_t *uuid,
  54. gatt_cb_t func, gpointer user_data)
  55. {
  56. unsigned int id;
  57. id = gatt_discover_desc(attrib, start, end, uuid, func, user_data);
  58. queue_push_head(scpp->gatt_op, UINT_TO_PTR(id));
  59. }
  60. static void write_char(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle,
  61. const uint8_t *value, size_t vlen,
  62. GAttribResultFunc func,
  63. gpointer user_data)
  64. {
  65. unsigned int id;
  66. id = gatt_write_char(attrib, handle, value, vlen, func, user_data);
  67. queue_push_head(scan->gatt_op, UINT_TO_PTR(id));
  68. }
  69. static void scpp_free(struct bt_scpp *scan)
  70. {
  71. bt_scpp_detach(scan);
  72. g_free(scan->primary);
  73. queue_destroy(scan->gatt_op, NULL); /* cleared in bt_scpp_detach */
  74. g_free(scan);
  75. }
  76. struct bt_scpp *bt_scpp_new(void *primary)
  77. {
  78. struct bt_scpp *scan;
  79. scan = g_try_new0(struct bt_scpp, 1);
  80. if (!scan)
  81. return NULL;
  82. scan->interval = SCAN_INTERVAL;
  83. scan->window = SCAN_WINDOW;
  84. scan->gatt_op = queue_new();
  85. if (primary)
  86. scan->primary = g_memdup(primary, sizeof(*scan->primary));
  87. return bt_scpp_ref(scan);
  88. }
  89. struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan)
  90. {
  91. if (!scan)
  92. return NULL;
  93. __sync_fetch_and_add(&scan->ref_count, 1);
  94. return scan;
  95. }
  96. void bt_scpp_unref(struct bt_scpp *scan)
  97. {
  98. if (!scan)
  99. return;
  100. if (__sync_sub_and_fetch(&scan->ref_count, 1))
  101. return;
  102. scpp_free(scan);
  103. }
  104. static void write_scan_params(GAttrib *attrib, uint16_t handle,
  105. uint16_t interval, uint16_t window)
  106. {
  107. uint8_t value[4];
  108. put_le16(interval, &value[0]);
  109. put_le16(window, &value[2]);
  110. gatt_write_cmd(attrib, handle, value, sizeof(value), NULL, NULL);
  111. }
  112. static void refresh_value_cb(const uint8_t *pdu, uint16_t len,
  113. gpointer user_data)
  114. {
  115. struct bt_scpp *scan = user_data;
  116. DBG("Server requires refresh: %d", pdu[3]);
  117. if (pdu[3] == SERVER_REQUIRES_REFRESH)
  118. write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
  119. scan->window);
  120. }
  121. static void ccc_written_cb(guint8 status, const guint8 *pdu,
  122. guint16 plen, gpointer user_data)
  123. {
  124. struct bt_scpp *scan = user_data;
  125. if (status != 0) {
  126. error("Write Scan Refresh CCC failed: %s",
  127. att_ecode2str(status));
  128. return;
  129. }
  130. DBG("Scan Refresh: notification enabled");
  131. scan->refresh_cb_id = g_attrib_register(scan->attrib,
  132. ATT_OP_HANDLE_NOTIFY, scan->refresh_handle,
  133. refresh_value_cb, scan, NULL);
  134. }
  135. static void write_ccc(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle,
  136. void *user_data)
  137. {
  138. uint8_t value[2];
  139. put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
  140. write_char(scan, attrib, handle, value, sizeof(value), ccc_written_cb,
  141. user_data);
  142. }
  143. static void discover_descriptor_cb(uint8_t status, GSList *descs,
  144. void *user_data)
  145. {
  146. struct bt_scpp *scan = user_data;
  147. struct gatt_desc *desc;
  148. if (status != 0) {
  149. error("Discover descriptors failed: %s", att_ecode2str(status));
  150. return;
  151. }
  152. /* There will be only one descriptor on list and it will be CCC */
  153. desc = descs->data;
  154. write_ccc(scan, scan->attrib, desc->handle, scan);
  155. }
  156. static void refresh_discovered_cb(uint8_t status, GSList *chars,
  157. void *user_data)
  158. {
  159. struct bt_scpp *scan = user_data;
  160. struct gatt_char *chr;
  161. uint16_t start, end;
  162. bt_uuid_t uuid;
  163. if (status) {
  164. error("Scan Refresh %s", att_ecode2str(status));
  165. return;
  166. }
  167. if (!chars) {
  168. DBG("Scan Refresh not supported");
  169. return;
  170. }
  171. chr = chars->data;
  172. DBG("Scan Refresh handle: 0x%04x", chr->value_handle);
  173. start = chr->value_handle + 1;
  174. end = scan->primary->range.end;
  175. if (start > end)
  176. return;
  177. scan->refresh_handle = chr->value_handle;
  178. bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
  179. discover_desc(scan, scan->attrib, start, end, &uuid,
  180. discover_descriptor_cb, user_data);
  181. }
  182. static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data)
  183. {
  184. struct bt_scpp *scan = user_data;
  185. struct gatt_char *chr;
  186. if (status) {
  187. error("Discover Scan Interval Window: %s",
  188. att_ecode2str(status));
  189. return;
  190. }
  191. chr = chars->data;
  192. scan->iwhandle = chr->value_handle;
  193. DBG("Scan Interval Window handle: 0x%04x", scan->iwhandle);
  194. write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
  195. scan->window);
  196. }
  197. bool bt_scpp_attach(struct bt_scpp *scan, void *attrib)
  198. {
  199. bt_uuid_t iwin_uuid, refresh_uuid;
  200. if (!scan || scan->attrib || !scan->primary)
  201. return false;
  202. scan->attrib = g_attrib_ref(attrib);
  203. if (scan->iwhandle)
  204. write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
  205. scan->window);
  206. else {
  207. bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID);
  208. discover_char(scan, scan->attrib, scan->primary->range.start,
  209. scan->primary->range.end, &iwin_uuid,
  210. iwin_discovered_cb, scan);
  211. }
  212. if (scan->refresh_handle)
  213. scan->refresh_cb_id = g_attrib_register(scan->attrib,
  214. ATT_OP_HANDLE_NOTIFY, scan->refresh_handle,
  215. refresh_value_cb, scan, NULL);
  216. else {
  217. bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID);
  218. discover_char(scan, scan->attrib, scan->primary->range.start,
  219. scan->primary->range.end, &refresh_uuid,
  220. refresh_discovered_cb, scan);
  221. }
  222. return true;
  223. }
  224. static void cancel_gatt_req(void *data, void *user_data)
  225. {
  226. unsigned int id = PTR_TO_UINT(data);
  227. struct bt_scpp *scan = user_data;
  228. g_attrib_cancel(scan->attrib, id);
  229. }
  230. void bt_scpp_detach(struct bt_scpp *scan)
  231. {
  232. if (!scan || !scan->attrib)
  233. return;
  234. if (scan->refresh_cb_id > 0) {
  235. g_attrib_unregister(scan->attrib, scan->refresh_cb_id);
  236. scan->refresh_cb_id = 0;
  237. }
  238. queue_foreach(scan->gatt_op, cancel_gatt_req, scan);
  239. g_attrib_unref(scan->attrib);
  240. scan->attrib = NULL;
  241. }
  242. bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value)
  243. {
  244. if (!scan)
  245. return false;
  246. /* TODO: Check valid range */
  247. scan->interval = value;
  248. return true;
  249. }
  250. bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value)
  251. {
  252. if (!scan)
  253. return false;
  254. /* TODO: Check valid range */
  255. scan->window = value;
  256. return true;
  257. }