| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2014 Intel Corporation
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #define _GNU_SOURCE
- #include <stdio.h>
- #include <stdlib.h>
- #include <getopt.h>
- #include <unistd.h>
- #include <glib.h>
- #include "lib/bluetooth.h"
- #include "lib/hci.h"
- #include "lib/hci_lib.h"
- #include "btio/btio.h"
- #include "lib/l2cap.h"
- #include "profiles/health/mcap.h"
- enum {
- MODE_NONE,
- MODE_CONNECT,
- MODE_LISTEN,
- };
- static GMainLoop *mloop;
- static int ccpsm = 0x1003, dcpsm = 0x1005;
- static struct mcap_instance *mcap = NULL;
- static struct mcap_mdl *mdl = NULL;
- static uint16_t mdlid;
- static int control_mode = MODE_LISTEN;
- static int data_mode = MODE_LISTEN;
- static int mdl_conn_req_result = MCAP_SUCCESS;
- static gboolean send_synccap_req = FALSE;
- static gboolean mcl_disconnect = FALSE;
- static gboolean mdl_disconnect = FALSE;
- static int mcl_disconnect_timeout = -1;
- static int mdl_disconnect_timeout = -1;
- static struct mcap_mcl *mcl = NULL;
- static gboolean no_close = FALSE;
- #define REQ_CLOCK_ACC 0x1400
- static void mdl_close(struct mcap_mdl *mdl)
- {
- int fd = -1;
- printf("%s\n", __func__);
- if (mdl_disconnect_timeout >= 0)
- sleep(mdl_disconnect_timeout);
- fd = mcap_mdl_get_fd(mdl);
- if (fd > 0)
- close(fd);
- }
- static void mdl_connected_cb(struct mcap_mdl *mdl, void *data)
- {
- printf("%s\n", __func__);
- if (mdl_disconnect)
- mdl_close(mdl);
- }
- static void mdl_closed_cb(struct mcap_mdl *mdl, void *data)
- {
- printf("%s\n", __func__);
- if (mcl_disconnect && mcl_disconnect_timeout >= 0) {
- sleep(mcl_disconnect_timeout);
- printf("Closing MCAP communication link\n");
- mcap_close_mcl(mcl, TRUE);
- if (no_close)
- return;
- g_main_loop_quit(mloop);
- }
- }
- static void mdl_deleted_cb(struct mcap_mdl *mdl, void *data)
- {
- /* TODO */
- printf("%s\n", __func__);
- /* Disconnecting MDL latency timeout */
- if (mdl_disconnect_timeout >= 0)
- sleep(mdl_disconnect_timeout);
- }
- static void mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
- {
- /* TODO */
- printf("%s\n", __func__);
- }
- static uint8_t mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
- uint16_t mdlid, uint8_t *conf, void *data)
- {
- int ret;
- printf("%s\n", __func__);
- ret = mdl_conn_req_result;
- mdl_conn_req_result = MCAP_SUCCESS;
- return ret;
- }
- static uint8_t mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
- {
- printf("%s\n", __func__);
- return MCAP_SUCCESS;
- }
- static void create_mdl_cb(struct mcap_mdl *mcap_mdl, uint8_t type, GError *gerr,
- gpointer data);
- static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
- {
- GError *gerr = NULL;
- printf("%s\n", __func__);
- if (data_mode == MODE_CONNECT) {
- mcap_create_mdl(mcl, 1, 0, create_mdl_cb, NULL, NULL, &gerr);
- if (gerr) {
- printf("Could not connect MDL: %s\n", gerr->message);
- g_error_free(gerr);
- }
- }
- }
- static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
- {
- /* TODO */
- printf("%s\n", __func__);
- if (no_close)
- return;
- g_main_loop_quit(mloop);
- }
- static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
- {
- /* TODO */
- printf("%s\n", __func__);
- }
- static void connect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data)
- {
- mdlid = mcap_mdl_get_mdlid(mdl);
- printf("%s\n", __func__);
- if (mdlid == MCAP_MDLID_RESERVED)
- printf("MCAP mdlid is reserved");
- else
- printf("MDL %d connected\n", mdlid);
- }
- static void create_mdl_cb(struct mcap_mdl *mcap_mdl, uint8_t type, GError *gerr,
- gpointer data)
- {
- GError *err = NULL;
- printf("%s\n", __func__);
- if (gerr) {
- printf("MDL error: %s\n", gerr->message);
- if (!no_close)
- g_main_loop_quit(mloop);
- return;
- }
- if (mdl)
- mcap_mdl_unref(mdl);
- mdl = mcap_mdl_ref(mcap_mdl);
- if (!mcap_connect_mdl(mdl, L2CAP_MODE_ERTM, dcpsm, connect_mdl_cb, NULL,
- NULL, &err)) {
- printf("Error connecting to mdl: %s\n", err->message);
- g_error_free(err);
- if (no_close)
- return;
- g_main_loop_quit(mloop);
- }
- }
- static void sync_cap_cb(struct mcap_mcl *mcl, uint8_t mcap_err,
- uint8_t btclockres, uint16_t synclead,
- uint16_t tmstampres, uint16_t tmstampacc, GError *err,
- gpointer data)
- {
- /* TODO */
- printf("%s\n", __func__);
- }
- static void trigger_mdl_action(int mode)
- {
- GError *gerr = NULL;
- gboolean ret;
- ret = mcap_mcl_set_cb(mcl, NULL, &gerr,
- MCAP_MDL_CB_CONNECTED, mdl_connected_cb,
- MCAP_MDL_CB_CLOSED, mdl_closed_cb,
- MCAP_MDL_CB_DELETED, mdl_deleted_cb,
- MCAP_MDL_CB_ABORTED, mdl_aborted_cb,
- MCAP_MDL_CB_REMOTE_CONN_REQ, mdl_conn_req_cb,
- MCAP_MDL_CB_REMOTE_RECONN_REQ, mdl_reconn_req_cb,
- MCAP_MDL_CB_INVALID);
- if (!ret && gerr) {
- printf("MCL cannot handle connection %s\n",
- gerr->message);
- g_error_free(gerr);
- }
- if (mode == MODE_CONNECT) {
- printf("Creating MCAP Data End Point\n");
- mcap_create_mdl(mcl, 1, 0, create_mdl_cb, NULL, NULL, &gerr);
- if (gerr) {
- printf("Could not connect MDL: %s\n", gerr->message);
- g_error_free(gerr);
- }
- }
- if (send_synccap_req && mcap->csp_enabled) {
- mcap_sync_init(mcl);
- mcap_sync_cap_req(mcl, REQ_CLOCK_ACC, sync_cap_cb, NULL, &gerr);
- if (gerr) {
- printf("MCAP Sync req error: %s\n", gerr->message);
- g_error_free(gerr);
- }
- }
- }
- static void mcl_connected(struct mcap_mcl *mcap_mcl, gpointer data)
- {
- printf("%s\n", __func__);
- if (mcl) {
- mcap_sync_stop(mcl);
- mcap_mcl_unref(mcl);
- }
- mcl = mcap_mcl_ref(mcap_mcl);
- trigger_mdl_action(data_mode);
- }
- static void create_mcl_cb(struct mcap_mcl *mcap_mcl, GError *err, gpointer data)
- {
- printf("%s\n", __func__);
- if (err) {
- printf("Could not connect MCL: %s\n", err->message);
- if (!no_close)
- g_main_loop_quit(mloop);
- return;
- }
- if (mcl) {
- mcap_sync_stop(mcl);
- mcap_mcl_unref(mcl);
- }
- mcl = mcap_mcl_ref(mcap_mcl);
- trigger_mdl_action(data_mode);
- }
- static void usage(void)
- {
- printf("mcaptest - MCAP testing ver %s\n", VERSION);
- printf("Usage:\n"
- "\tmcaptest <control_mode> <data_mode> [options]\n");
- printf("Control Link Mode:\n"
- "\t-c connect <dst_addr>\n"
- "\t-b close control link after closing data link\n"
- "\t-e <timeout> disconnect MCL and quit after MDL is closed\n"
- "\t-g send clock sync capability request if MCL connected\n");
- printf("Data Link Mode:\n"
- "\t-d connect\n"
- "\t-a close data link immediately after being connected"
- "\t-f <timeout> disconnect MDL after it's connected\n"
- "\t-u send \'Unavailable\' on first MDL connection request\n");
- printf("Options:\n"
- "\t-n don't exit after mcl disconnect/err receive\n"
- "\t-i <hcidev> HCI device\n"
- "\t-C <control_ch> Control channel PSM\n"
- "\t-D <data_ch> Data channel PSM\n");
- }
- static struct option main_options[] = {
- { "help", 0, 0, 'h' },
- { "device", 1, 0, 'i' },
- { "connect_cl", 1, 0, 'c' },
- { "disconnect_cl", 1, 0, 'e' },
- { "synccap_req", 0, 0, 'g' },
- { "connect_dl", 0, 0, 'd' },
- { "disconnect_da", 0, 0, 'a' },
- { "disconnect_ca", 0, 0, 'b' },
- { "disconnect_dl", 1, 0, 'f' },
- { "unavailable_dl", 0, 0, 'u' },
- { "no exit mcl dis/err",0, 0, 'n' },
- { "control_ch", 1, 0, 'C' },
- { "data_ch", 1, 0, 'D' },
- { 0, 0, 0, 0 }
- };
- int main(int argc, char *argv[])
- {
- GError *err = NULL;
- bdaddr_t src, dst;
- int opt;
- char bdastr[18];
- hci_devba(0, &src);
- bacpy(&dst, BDADDR_ANY);
- mloop = g_main_loop_new(NULL, FALSE);
- if (!mloop) {
- printf("Cannot create main loop\n");
- exit(1);
- }
- while ((opt = getopt_long(argc, argv, "+i:c:C:D:e:f:dghunab",
- main_options, NULL)) != EOF) {
- switch (opt) {
- case 'i':
- if (!strncmp(optarg, "hci", 3))
- hci_devba(atoi(optarg + 3), &src);
- else
- str2ba(optarg, &src);
- break;
- case 'c':
- control_mode = MODE_CONNECT;
- str2ba(optarg, &dst);
- break;
- case 'd':
- data_mode = MODE_CONNECT;
- break;
- case 'a':
- mdl_disconnect = TRUE;
- break;
- case 'b':
- mcl_disconnect = TRUE;
- break;
- case 'e':
- mcl_disconnect_timeout = atoi(optarg);
- break;
- case 'f':
- mdl_disconnect_timeout = atoi(optarg);
- break;
- case 'g':
- send_synccap_req = TRUE;
- break;
- case 'u':
- mdl_conn_req_result = MCAP_RESOURCE_UNAVAILABLE;
- break;
- case 'n':
- no_close = TRUE;
- break;
- case 'C':
- ccpsm = atoi(optarg);
- break;
- case 'D':
- dcpsm = atoi(optarg);
- break;
- case 'h':
- default:
- usage();
- exit(0);
- }
- }
- mcap = mcap_create_instance(&src, BT_IO_SEC_MEDIUM, ccpsm, dcpsm,
- mcl_connected, mcl_reconnected,
- mcl_disconnected, mcl_uncached,
- NULL, /* CSP is not used right now */
- NULL, &err);
- if (!mcap) {
- printf("MCAP instance creation failed %s\n", err->message);
- g_error_free(err);
- exit(1);
- }
- mcap_enable_csp(mcap);
- switch (control_mode) {
- case MODE_CONNECT:
- ba2str(&dst, bdastr);
- printf("Connecting to %s\n", bdastr);
- mcap_create_mcl(mcap, &dst, ccpsm, create_mcl_cb, NULL, NULL,
- &err);
- if (err) {
- printf("MCAP create error %s\n", err->message);
- g_error_free(err);
- exit(1);
- }
- break;
- case MODE_LISTEN:
- printf("Listening for control channel connection\n");
- break;
- case MODE_NONE:
- default:
- goto done;
- }
- g_main_loop_run(mloop);
- done:
- printf("Done\n");
- if (mcap)
- mcap_instance_unref(mcap);
- g_main_loop_unref(mloop);
- return 0;
- }
|