mcaptest.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. *
  4. * BlueZ - Bluetooth protocol stack for Linux
  5. *
  6. * Copyright (C) 2014 Intel Corporation
  7. *
  8. *
  9. */
  10. #ifdef HAVE_CONFIG_H
  11. #include <config.h>
  12. #endif
  13. #define _GNU_SOURCE
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <getopt.h>
  17. #include <unistd.h>
  18. #include <glib.h>
  19. #include "lib/bluetooth.h"
  20. #include "lib/hci.h"
  21. #include "lib/hci_lib.h"
  22. #include "btio/btio.h"
  23. #include "lib/l2cap.h"
  24. #include "profiles/health/mcap.h"
  25. enum {
  26. MODE_NONE,
  27. MODE_CONNECT,
  28. MODE_LISTEN,
  29. };
  30. static GMainLoop *mloop;
  31. static int ccpsm = 0x1003, dcpsm = 0x1005;
  32. static struct mcap_instance *mcap = NULL;
  33. static struct mcap_mdl *mdl = NULL;
  34. static uint16_t mdlid;
  35. static int control_mode = MODE_LISTEN;
  36. static int data_mode = MODE_LISTEN;
  37. static int mdl_conn_req_result = MCAP_SUCCESS;
  38. static gboolean send_synccap_req = FALSE;
  39. static gboolean mcl_disconnect = FALSE;
  40. static gboolean mdl_disconnect = FALSE;
  41. static int mcl_disconnect_timeout = -1;
  42. static int mdl_disconnect_timeout = -1;
  43. static struct mcap_mcl *mcl = NULL;
  44. static gboolean no_close = FALSE;
  45. #define REQ_CLOCK_ACC 0x1400
  46. static void mdl_close(struct mcap_mdl *mdl)
  47. {
  48. int fd = -1;
  49. printf("%s\n", __func__);
  50. if (mdl_disconnect_timeout >= 0)
  51. sleep(mdl_disconnect_timeout);
  52. fd = mcap_mdl_get_fd(mdl);
  53. if (fd > 0)
  54. close(fd);
  55. }
  56. static void mdl_connected_cb(struct mcap_mdl *mdl, void *data)
  57. {
  58. printf("%s\n", __func__);
  59. if (mdl_disconnect)
  60. mdl_close(mdl);
  61. }
  62. static void mdl_closed_cb(struct mcap_mdl *mdl, void *data)
  63. {
  64. printf("%s\n", __func__);
  65. if (mcl_disconnect && mcl_disconnect_timeout >= 0) {
  66. sleep(mcl_disconnect_timeout);
  67. printf("Closing MCAP communication link\n");
  68. mcap_close_mcl(mcl, TRUE);
  69. if (no_close)
  70. return;
  71. g_main_loop_quit(mloop);
  72. }
  73. }
  74. static void mdl_deleted_cb(struct mcap_mdl *mdl, void *data)
  75. {
  76. /* TODO */
  77. printf("%s\n", __func__);
  78. /* Disconnecting MDL latency timeout */
  79. if (mdl_disconnect_timeout >= 0)
  80. sleep(mdl_disconnect_timeout);
  81. }
  82. static void mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
  83. {
  84. /* TODO */
  85. printf("%s\n", __func__);
  86. }
  87. static uint8_t mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
  88. uint16_t mdlid, uint8_t *conf, void *data)
  89. {
  90. int ret;
  91. printf("%s\n", __func__);
  92. ret = mdl_conn_req_result;
  93. mdl_conn_req_result = MCAP_SUCCESS;
  94. return ret;
  95. }
  96. static uint8_t mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
  97. {
  98. printf("%s\n", __func__);
  99. return MCAP_SUCCESS;
  100. }
  101. static void create_mdl_cb(struct mcap_mdl *mcap_mdl, uint8_t type, GError *gerr,
  102. gpointer data);
  103. static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
  104. {
  105. GError *gerr = NULL;
  106. printf("%s\n", __func__);
  107. if (data_mode == MODE_CONNECT) {
  108. mcap_create_mdl(mcl, 1, 0, create_mdl_cb, NULL, NULL, &gerr);
  109. if (gerr) {
  110. printf("Could not connect MDL: %s\n", gerr->message);
  111. g_error_free(gerr);
  112. }
  113. }
  114. }
  115. static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
  116. {
  117. /* TODO */
  118. printf("%s\n", __func__);
  119. if (no_close)
  120. return;
  121. g_main_loop_quit(mloop);
  122. }
  123. static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
  124. {
  125. /* TODO */
  126. printf("%s\n", __func__);
  127. }
  128. static void connect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data)
  129. {
  130. mdlid = mcap_mdl_get_mdlid(mdl);
  131. printf("%s\n", __func__);
  132. if (mdlid == MCAP_MDLID_RESERVED)
  133. printf("MCAP mdlid is reserved");
  134. else
  135. printf("MDL %d connected\n", mdlid);
  136. }
  137. static void create_mdl_cb(struct mcap_mdl *mcap_mdl, uint8_t type, GError *gerr,
  138. gpointer data)
  139. {
  140. GError *err = NULL;
  141. printf("%s\n", __func__);
  142. if (gerr) {
  143. printf("MDL error: %s\n", gerr->message);
  144. if (!no_close)
  145. g_main_loop_quit(mloop);
  146. return;
  147. }
  148. if (mdl)
  149. mcap_mdl_unref(mdl);
  150. mdl = mcap_mdl_ref(mcap_mdl);
  151. if (!mcap_connect_mdl(mdl, L2CAP_MODE_ERTM, dcpsm, connect_mdl_cb, NULL,
  152. NULL, &err)) {
  153. printf("Error connecting to mdl: %s\n", err->message);
  154. g_error_free(err);
  155. if (no_close)
  156. return;
  157. g_main_loop_quit(mloop);
  158. }
  159. }
  160. static void sync_cap_cb(struct mcap_mcl *mcl, uint8_t mcap_err,
  161. uint8_t btclockres, uint16_t synclead,
  162. uint16_t tmstampres, uint16_t tmstampacc, GError *err,
  163. gpointer data)
  164. {
  165. /* TODO */
  166. printf("%s\n", __func__);
  167. }
  168. static void trigger_mdl_action(int mode)
  169. {
  170. GError *gerr = NULL;
  171. gboolean ret;
  172. ret = mcap_mcl_set_cb(mcl, NULL, &gerr,
  173. MCAP_MDL_CB_CONNECTED, mdl_connected_cb,
  174. MCAP_MDL_CB_CLOSED, mdl_closed_cb,
  175. MCAP_MDL_CB_DELETED, mdl_deleted_cb,
  176. MCAP_MDL_CB_ABORTED, mdl_aborted_cb,
  177. MCAP_MDL_CB_REMOTE_CONN_REQ, mdl_conn_req_cb,
  178. MCAP_MDL_CB_REMOTE_RECONN_REQ, mdl_reconn_req_cb,
  179. MCAP_MDL_CB_INVALID);
  180. if (!ret && gerr) {
  181. printf("MCL cannot handle connection %s\n",
  182. gerr->message);
  183. g_error_free(gerr);
  184. }
  185. if (mode == MODE_CONNECT) {
  186. printf("Creating MCAP Data End Point\n");
  187. mcap_create_mdl(mcl, 1, 0, create_mdl_cb, NULL, NULL, &gerr);
  188. if (gerr) {
  189. printf("Could not connect MDL: %s\n", gerr->message);
  190. g_error_free(gerr);
  191. }
  192. }
  193. if (send_synccap_req && mcap->csp_enabled) {
  194. mcap_sync_init(mcl);
  195. mcap_sync_cap_req(mcl, REQ_CLOCK_ACC, sync_cap_cb, NULL, &gerr);
  196. if (gerr) {
  197. printf("MCAP Sync req error: %s\n", gerr->message);
  198. g_error_free(gerr);
  199. }
  200. }
  201. }
  202. static void mcl_connected(struct mcap_mcl *mcap_mcl, gpointer data)
  203. {
  204. printf("%s\n", __func__);
  205. if (mcl) {
  206. mcap_sync_stop(mcl);
  207. mcap_mcl_unref(mcl);
  208. }
  209. mcl = mcap_mcl_ref(mcap_mcl);
  210. trigger_mdl_action(data_mode);
  211. }
  212. static void create_mcl_cb(struct mcap_mcl *mcap_mcl, GError *err, gpointer data)
  213. {
  214. printf("%s\n", __func__);
  215. if (err) {
  216. printf("Could not connect MCL: %s\n", err->message);
  217. if (!no_close)
  218. g_main_loop_quit(mloop);
  219. return;
  220. }
  221. if (mcl) {
  222. mcap_sync_stop(mcl);
  223. mcap_mcl_unref(mcl);
  224. }
  225. mcl = mcap_mcl_ref(mcap_mcl);
  226. trigger_mdl_action(data_mode);
  227. }
  228. static void usage(void)
  229. {
  230. printf("mcaptest - MCAP testing ver %s\n", VERSION);
  231. printf("Usage:\n"
  232. "\tmcaptest <control_mode> <data_mode> [options]\n");
  233. printf("Control Link Mode:\n"
  234. "\t-c connect <dst_addr>\n"
  235. "\t-b close control link after closing data link\n"
  236. "\t-e <timeout> disconnect MCL and quit after MDL is closed\n"
  237. "\t-g send clock sync capability request if MCL connected\n");
  238. printf("Data Link Mode:\n"
  239. "\t-d connect\n"
  240. "\t-a close data link immediately after being connected"
  241. "\t-f <timeout> disconnect MDL after it's connected\n"
  242. "\t-u send \'Unavailable\' on first MDL connection request\n");
  243. printf("Options:\n"
  244. "\t-n don't exit after mcl disconnect/err receive\n"
  245. "\t-i <hcidev> HCI device\n"
  246. "\t-C <control_ch> Control channel PSM\n"
  247. "\t-D <data_ch> Data channel PSM\n");
  248. }
  249. static struct option main_options[] = {
  250. { "help", 0, 0, 'h' },
  251. { "device", 1, 0, 'i' },
  252. { "connect_cl", 1, 0, 'c' },
  253. { "disconnect_cl", 1, 0, 'e' },
  254. { "synccap_req", 0, 0, 'g' },
  255. { "connect_dl", 0, 0, 'd' },
  256. { "disconnect_da", 0, 0, 'a' },
  257. { "disconnect_ca", 0, 0, 'b' },
  258. { "disconnect_dl", 1, 0, 'f' },
  259. { "unavailable_dl", 0, 0, 'u' },
  260. { "no exit mcl dis/err",0, 0, 'n' },
  261. { "control_ch", 1, 0, 'C' },
  262. { "data_ch", 1, 0, 'D' },
  263. { 0, 0, 0, 0 }
  264. };
  265. int main(int argc, char *argv[])
  266. {
  267. GError *err = NULL;
  268. bdaddr_t src, dst;
  269. int opt;
  270. char bdastr[18];
  271. hci_devba(0, &src);
  272. bacpy(&dst, BDADDR_ANY);
  273. mloop = g_main_loop_new(NULL, FALSE);
  274. if (!mloop) {
  275. printf("Cannot create main loop\n");
  276. exit(1);
  277. }
  278. while ((opt = getopt_long(argc, argv, "+i:c:C:D:e:f:dghunab",
  279. main_options, NULL)) != EOF) {
  280. switch (opt) {
  281. case 'i':
  282. if (!strncmp(optarg, "hci", 3))
  283. hci_devba(atoi(optarg + 3), &src);
  284. else
  285. str2ba(optarg, &src);
  286. break;
  287. case 'c':
  288. control_mode = MODE_CONNECT;
  289. str2ba(optarg, &dst);
  290. break;
  291. case 'd':
  292. data_mode = MODE_CONNECT;
  293. break;
  294. case 'a':
  295. mdl_disconnect = TRUE;
  296. break;
  297. case 'b':
  298. mcl_disconnect = TRUE;
  299. break;
  300. case 'e':
  301. mcl_disconnect_timeout = atoi(optarg);
  302. break;
  303. case 'f':
  304. mdl_disconnect_timeout = atoi(optarg);
  305. break;
  306. case 'g':
  307. send_synccap_req = TRUE;
  308. break;
  309. case 'u':
  310. mdl_conn_req_result = MCAP_RESOURCE_UNAVAILABLE;
  311. break;
  312. case 'n':
  313. no_close = TRUE;
  314. break;
  315. case 'C':
  316. ccpsm = atoi(optarg);
  317. break;
  318. case 'D':
  319. dcpsm = atoi(optarg);
  320. break;
  321. case 'h':
  322. default:
  323. usage();
  324. exit(0);
  325. }
  326. }
  327. mcap = mcap_create_instance(&src, BT_IO_SEC_MEDIUM, ccpsm, dcpsm,
  328. mcl_connected, mcl_reconnected,
  329. mcl_disconnected, mcl_uncached,
  330. NULL, /* CSP is not used right now */
  331. NULL, &err);
  332. if (!mcap) {
  333. printf("MCAP instance creation failed %s\n", err->message);
  334. g_error_free(err);
  335. exit(1);
  336. }
  337. mcap_enable_csp(mcap);
  338. switch (control_mode) {
  339. case MODE_CONNECT:
  340. ba2str(&dst, bdastr);
  341. printf("Connecting to %s\n", bdastr);
  342. mcap_create_mcl(mcap, &dst, ccpsm, create_mcl_cb, NULL, NULL,
  343. &err);
  344. if (err) {
  345. printf("MCAP create error %s\n", err->message);
  346. g_error_free(err);
  347. exit(1);
  348. }
  349. break;
  350. case MODE_LISTEN:
  351. printf("Listening for control channel connection\n");
  352. break;
  353. case MODE_NONE:
  354. default:
  355. goto done;
  356. }
  357. g_main_loop_run(mloop);
  358. done:
  359. printf("Done\n");
  360. if (mcap)
  361. mcap_instance_unref(mcap);
  362. g_main_loop_unref(mloop);
  363. return 0;
  364. }