MCAP: Add a test tool for MCAP
* Add a command line tool for MCAP related tests
* This tool can access the Bluetooth stack via a command line program
* Type "help" to obtain a list of available console commands in this
Bluetooth
* Bluetooth must be disabled when this tool is running
Bug: 37867299
Test: make, PTS MCAP test
Change-Id: I106e6625ee8a29b7fdad88cdfeb99419054ef771
diff --git a/Android.bp b/Android.bp
index 69ed54b..dae6460 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,4 +15,5 @@
"vendor_libs",
"test",
"udrv",
+ "tools",
]
diff --git a/BUILD.gn b/BUILD.gn
index 3cb29e1..618de1c 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -39,3 +39,10 @@
"//device:net_test_device",
]
}
+
+group("test_tools") {
+ testonly = true
+ deps = [
+ "//tools/mcap_tool:mcap_tool"
+ ]
+}
diff --git a/tools/Android.bp b/tools/Android.bp
new file mode 100644
index 0000000..1357a93
--- /dev/null
+++ b/tools/Android.bp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+subdirs = [
+ "mcap_tool",
+]
diff --git a/tools/mcap_tool/Android.bp b/tools/mcap_tool/Android.bp
new file mode 100644
index 0000000..aca07dd
--- /dev/null
+++ b/tools/mcap_tool/Android.bp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+cc_binary {
+ name: "mcap_tool",
+ defaults : ["fluoride_defaults"],
+ srcs: [
+ "mcap_test_app.cc",
+ "mcap_test_mcl.cc",
+ "mcap_test_mdep.cc",
+ "mcap_test_mdl.cc",
+ "mcap_tool.cc",
+ ],
+ include_dirs: [
+ "system/bt",
+ "system/bt/include",
+ "system/bt/stack/include",
+ "system/bt/btcore/include",
+ ],
+ tags: ["debug", "optional"],
+ shared_libs: [
+ "libcutils",
+ "libutils",
+ "libhardware",
+ ],
+ static_libs: [
+ "libbtcore",
+ "libosi",
+ ]
+}
diff --git a/tools/mcap_tool/BUILD.gn b/tools/mcap_tool/BUILD.gn
new file mode 100644
index 0000000..a5fef31
--- /dev/null
+++ b/tools/mcap_tool/BUILD.gn
@@ -0,0 +1,43 @@
+#
+# Copyright (C) 2017 Google, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+executable("mcap_tool") {
+ testonly = true
+ sources = [
+ "mcap_tool.cc",
+ "mcap_test_app.cc",
+ "mcap_test_mcl.cc",
+ "mcap_test_mdep.cc",
+ "mcap_test_mdl.cc",
+ ]
+ include_dirs = [
+ "//",
+ "//include",
+ "//stack/include",
+ "//btif/include",
+ "//btcore/include",
+ "//tools/mcap_tool",
+ ]
+ libs = [
+ "-lpthread",
+ "-lrt",
+ "-ldl",
+ ]
+ deps = [
+ "//btcore",
+ "//osi",
+ "//third_party/libchrome:base",
+ ]
+}
diff --git a/tools/mcap_tool/mcap_test_app.cc b/tools/mcap_tool/mcap_test_app.cc
new file mode 100644
index 0000000..29e50dc
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_app.cc
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "mcap_test_app.h"
+#include "mca_defs.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+#define CASE_RETURN_STR(const) \
+ case const: \
+ return #const;
+
+static const char* dump_mcap_events(const uint8_t event) {
+ switch (event) {
+ CASE_RETURN_STR(MCA_ERROR_RSP_EVT)
+ CASE_RETURN_STR(MCA_CREATE_IND_EVT)
+ CASE_RETURN_STR(MCA_CREATE_CFM_EVT)
+ CASE_RETURN_STR(MCA_RECONNECT_IND_EVT)
+ CASE_RETURN_STR(MCA_RECONNECT_CFM_EVT)
+ CASE_RETURN_STR(MCA_ABORT_IND_EVT)
+ CASE_RETURN_STR(MCA_ABORT_CFM_EVT)
+ CASE_RETURN_STR(MCA_DELETE_IND_EVT)
+ CASE_RETURN_STR(MCA_DELETE_CFM_EVT)
+ CASE_RETURN_STR(MCA_SYNC_CAP_IND_EVT)
+ CASE_RETURN_STR(MCA_SYNC_CAP_CFM_EVT)
+ CASE_RETURN_STR(MCA_SYNC_SET_IND_EVT)
+ CASE_RETURN_STR(MCA_SYNC_SET_CFM_EVT)
+ CASE_RETURN_STR(MCA_SYNC_INFO_IND_EVT)
+ CASE_RETURN_STR(MCA_CONNECT_IND_EVT)
+ CASE_RETURN_STR(MCA_DISCONNECT_IND_EVT)
+ CASE_RETURN_STR(MCA_OPEN_IND_EVT)
+ CASE_RETURN_STR(MCA_OPEN_CFM_EVT)
+ CASE_RETURN_STR(MCA_CLOSE_IND_EVT)
+ CASE_RETURN_STR(MCA_CLOSE_CFM_EVT)
+ CASE_RETURN_STR(MCA_CONG_CHG_EVT)
+ CASE_RETURN_STR(MCA_RSP_TOUT_IND_EVT)
+ default:
+ return "Unknown event";
+ }
+}
+
+static void print_mcap_event(const tMCA_DISCONNECT_IND* mcap_disconnect_ind) {
+ bdstr_t bd_addr_str;
+ printf("%s: peer_bd_addr=%s,l2cap_disconnect_reason=0x%04x\n", __func__,
+ bdaddr_to_string((bt_bdaddr_t*)mcap_disconnect_ind->bd_addr,
+ bd_addr_str, sizeof(bd_addr_str)),
+ mcap_disconnect_ind->reason);
+}
+
+static void print_mcap_event(const tMCA_CONNECT_IND* mcap_connect_ind) {
+ bdstr_t bd_addr_str;
+ printf("%s: peer_bd_addr=%s, peer_mtu=%d \n", __func__,
+ bdaddr_to_string((bt_bdaddr_t*)mcap_connect_ind->bd_addr, bd_addr_str,
+ sizeof(bd_addr_str)),
+ mcap_connect_ind->mtu);
+}
+
+static void print_mcap_event(const tMCA_RSP_EVT* mcap_rsp) {
+ printf("%s: response, mdl_id=%d, op_code=0x%02x, rsp_code=0x%02x\n", __func__,
+ mcap_rsp->mdl_id, mcap_rsp->op_code, mcap_rsp->rsp_code);
+}
+
+static void print_mcap_event(const tMCA_EVT_HDR* mcap_evt_hdr) {
+ printf("%s: event, mdl_id=%d, op_code=0x%02x\n", __func__,
+ mcap_evt_hdr->mdl_id, mcap_evt_hdr->op_code);
+}
+
+static void print_mcap_event(const tMCA_CREATE_IND* mcap_create_ind) {
+ printf("%s: mdl_id=%d, op_code=0x%02x, dep_id=%d, cfg=0x%02x\n", __func__,
+ mcap_create_ind->mdl_id, mcap_create_ind->op_code,
+ mcap_create_ind->dep_id, mcap_create_ind->cfg);
+}
+
+static void print_mcap_event(const tMCA_CREATE_CFM* mcap_create_cfm) {
+ printf("%s: mdl_id=%d, op_code=0x%02x, rsp_code=%d, cfg=0x%02x\n", __func__,
+ mcap_create_cfm->mdl_id, mcap_create_cfm->op_code,
+ mcap_create_cfm->rsp_code, mcap_create_cfm->cfg);
+}
+
+static void print_mcap_event(const tMCA_DL_OPEN* mcap_dl_open) {
+ printf("%s: mdl_id=%d, mdl_handle=%d, mtu=%d\n", __func__,
+ mcap_dl_open->mdl_id, mcap_dl_open->mdl, mcap_dl_open->mtu);
+}
+
+static void print_mcap_event(const tMCA_DL_CLOSE* mcap_dl_close) {
+ printf("%s: mdl_id=%d, mdl_handle=%d, l2cap_disconnect_reason=0x%04x\n",
+ __func__, mcap_dl_close->mdl_id, mcap_dl_close->mdl,
+ mcap_dl_close->reason);
+}
+
+static void print_mcap_event(const tMCA_CONG_CHG* mcap_congestion_change) {
+ printf("%s: mdl_id=%d, mdl_handle=%d, congested=%d\n", __func__,
+ mcap_congestion_change->mdl_id, mcap_congestion_change->mdl,
+ mcap_congestion_change->cong);
+}
+
+McapTestApp::McapTestApp(btmcap_test_interface_t* mcap_test_interface)
+ : _mcl_list(), _mdep_list() {
+ _mcap_test_interface = mcap_test_interface;
+}
+
+btmcap_test_interface_t* McapTestApp::GetInterface() {
+ return _mcap_test_interface;
+}
+
+bool McapTestApp::Register(uint16_t ctrl_psm, uint16_t data_psm,
+ uint16_t sec_mask, tMCA_CTRL_CBACK* callback) {
+ if (!callback) {
+ LOG(ERROR) << "callback is null";
+ return false;
+ }
+ _mca_reg.rsp_tout = 5000;
+ _mca_reg.ctrl_psm = ctrl_psm;
+ _mca_reg.data_psm = data_psm;
+ _mca_reg.sec_mask = sec_mask;
+ _mcap_handle =
+ _mcap_test_interface->register_application(&_mca_reg, callback);
+ return _mcap_handle > 0;
+}
+
+void McapTestApp::Deregister() {
+ _mcap_test_interface->deregister_application(_mcap_handle);
+ _mcap_handle = 0;
+}
+
+bool McapTestApp::Registered() { return _mcap_handle > 0; }
+
+bool McapTestApp::ConnectMcl(const bt_bdaddr_t& bd_addr, uint16_t ctrl_psm,
+ uint16_t sec_mask) {
+ if (!Registered()) {
+ LOG(ERROR) << "Application not registered";
+ return false;
+ }
+ McapMcl* mcap_mcl = FindMclByPeerAddress(bd_addr);
+ if (!mcap_mcl) {
+ LOG(INFO) << "MCL does not exist, creating new MCL";
+ _mcl_list.push_back(McapMcl(_mcap_test_interface, _mcap_handle, bd_addr));
+ mcap_mcl = &_mcl_list[_mcl_list.size() - 1];
+ }
+ if (mcap_mcl->GetHandle() != 0) {
+ LOG(ERROR) << "MCL is still active, cannot make another connection";
+ return false;
+ }
+ return mcap_mcl->Connect(ctrl_psm, sec_mask);
+}
+
+bool McapTestApp::CreateMdep(uint8_t type, uint8_t max_mdl,
+ tMCA_DATA_CBACK* data_callback) {
+ if (!data_callback) {
+ LOG(ERROR) << "Data callback is null";
+ return false;
+ }
+ _mdep_list.push_back(McapMdep(_mcap_test_interface, _mcap_handle, type,
+ max_mdl, data_callback));
+ return _mdep_list[_mdep_list.size() - 1].Create();
+}
+
+uint8_t McapTestApp::GetHandle() { return _mcap_handle; }
+
+McapMcl* McapTestApp::FindMclByPeerAddress(const bt_bdaddr_t& bd_addr) {
+ for (McapMcl& mcl : _mcl_list) {
+ if (bdaddr_equals(&mcl.GetPeerAddress(), &bd_addr)) {
+ return &mcl;
+ }
+ }
+ return nullptr;
+}
+
+McapMcl* McapTestApp::FindMclByHandle(tMCA_CL mcl_handle) {
+ for (McapMcl& mcl : _mcl_list) {
+ if (mcl.GetHandle() == mcl_handle) {
+ return &mcl;
+ }
+ }
+ return nullptr;
+}
+
+McapMdep* McapTestApp::FindMdepByHandle(tMCA_DEP mdep_handle) {
+ for (McapMdep& mdep : _mdep_list) {
+ if (mdep.GetHandle() == mdep_handle) {
+ return &mdep;
+ }
+ }
+ return nullptr;
+}
+
+void McapTestApp::RemoveMclByHandle(tMCA_CL mcl_handle) {
+ LOG(INFO) << "Removing MCL handle " << (int)mcl_handle;
+ for (std::vector<McapMcl>::iterator it = _mcl_list.begin();
+ it != _mcl_list.end(); ++it) {
+ if (it->GetHandle() == mcl_handle) {
+ _mcl_list.erase(it);
+ LOG(INFO) << "Removed MCL handle " << (int)mcl_handle;
+ return;
+ }
+ }
+}
+
+bool McapTestApp::IsRegistered() { return _mcap_handle > 0; }
+
+void McapTestApp::ControlCallback(tMCA_HANDLE handle, tMCA_CL mcl,
+ uint8_t event, tMCA_CTRL* p_data) {
+ McapMcl* mcap_mcl = FindMclByHandle(mcl);
+ McapMdl* mcap_mdl = nullptr;
+ printf("%s: mcap_handle=%d, mcl_handle=%d, event=%s (0x%02x)\n", __func__,
+ handle, mcl, dump_mcap_events(event), event);
+ if (_mcap_handle != handle) {
+ LOG(ERROR) << "MCAP handle mismatch, self=" << _mcap_handle
+ << ", other=" << handle;
+ return;
+ }
+ switch (event) {
+ case MCA_ERROR_RSP_EVT:
+ print_mcap_event(&p_data->rsp);
+ break;
+
+ case MCA_CREATE_CFM_EVT:
+ // Called when MCA_CreateMdl succeeded step 1 when response is received
+ print_mcap_event(&p_data->create_cfm);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+ break;
+ }
+ if (!mcap_mcl->IsConnected()) {
+ LOG(ERROR) << "MCL " << (int)mcl << " not connected";
+ break;
+ }
+ mcap_mdl = mcap_mcl->FindMdlById(p_data->create_cfm.mdl_id);
+ if (!mcap_mdl) {
+ LOG(ERROR) << "MDL not found for id " << p_data->create_cfm.mdl_id;
+ break;
+ }
+ if (mcap_mdl->GetResponseCode() >= 0) {
+ LOG(ERROR) << "MDL already got response " << mcap_mdl->GetResponseCode()
+ << " for id " << p_data->create_cfm.mdl_id;
+ break;
+ }
+ mcap_mdl->SetResponseCode(p_data->create_cfm.rsp_code);
+ break;
+
+ case MCA_CREATE_IND_EVT: {
+ // Should be replied with MCA_CreateMdlRsp
+ print_mcap_event(&p_data->create_ind);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+ break;
+ }
+ if (!mcap_mcl->IsConnected()) {
+ LOG(ERROR) << "MCL " << (int)mcl << " not connected";
+ break;
+ }
+ McapMdep* mcap_mdep = FindMdepByHandle(p_data->create_ind.dep_id);
+ if (!mcap_mdep) {
+ LOG(ERROR) << "MDEP ID " << (int)p_data->create_ind.dep_id
+ << " does not exist";
+ _mcap_test_interface->create_mdl_response(
+ mcl, p_data->create_ind.dep_id, p_data->create_ind.mdl_id, 0,
+ MCA_RSP_BAD_MDEP, get_test_channel_config());
+ break;
+ }
+ bool ret = mcap_mcl->CreateMdlResponse(
+ mcap_mdep->GetHandle(), p_data->create_ind.mdl_id,
+ p_data->create_ind.dep_id, p_data->create_ind.cfg);
+ LOG(INFO) << (ret ? "SUCCESS" : "FAIL");
+ if (!ret) {
+ _mcap_test_interface->create_mdl_response(
+ mcl, p_data->create_ind.dep_id, p_data->create_ind.mdl_id, 0,
+ MCA_RSP_NO_RESOURCE, get_test_channel_config());
+ }
+ break;
+ }
+ case MCA_RECONNECT_IND_EVT: {
+ // Called when remote device asks to reconnect
+ // reply with MCA_ReconnectMdlRsp
+ print_mcap_event(&p_data->reconnect_ind);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+ break;
+ }
+ if (!mcap_mcl->IsConnected()) {
+ LOG(ERROR) << "MCL " << (int)mcl << " not connected";
+ break;
+ }
+ mcap_mdl = mcap_mcl->FindMdlById(p_data->reconnect_ind.mdl_id);
+ if (mcap_mdl && !mcap_mdl->IsConnected()) {
+ LOG(INFO) << "Creating reconnect response for MDL "
+ << (int)p_data->reconnect_ind.mdl_id;
+ mcap_mdl->ReconnectResponse();
+ break;
+ }
+ LOG_IF(WARNING, mcap_mdl && mcap_mdl->IsConnected())
+ << "MDL ID " << (int)p_data->reconnect_ind.mdl_id
+ << " is already connected";
+ LOG_IF(WARNING, !mcap_mdl) << "No MDL for mdl_id "
+ << p_data->reconnect_ind.mdl_id;
+ tMCA_DEP mdep_handle = 0;
+ if (_mdep_list.size() > 0) {
+ mdep_handle = _mdep_list[0].GetHandle();
+ } else {
+ LOG(ERROR) << "Cannot find any available MDEP";
+ }
+ tMCA_RESULT ret = _mcap_test_interface->reconnect_mdl_response(
+ mcl, mdep_handle, p_data->reconnect_ind.mdl_id, MCA_RSP_BAD_MDL,
+ get_test_channel_config());
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << ret;
+ break;
+ }
+ case MCA_RECONNECT_CFM_EVT:
+ // Called when MCA_ReconnectMdl step 1, receives a response
+ print_mcap_event(&p_data->reconnect_cfm);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+ break;
+ }
+ if (!mcap_mcl->IsConnected()) {
+ LOG(ERROR) << "MCL not connected for mcl_handle " << (int)mcl;
+ break;
+ }
+ mcap_mdl = mcap_mcl->FindMdlById(p_data->reconnect_cfm.mdl_id);
+ if (!mcap_mdl) {
+ LOG(ERROR) << "MDL not found for id " << p_data->reconnect_cfm.mdl_id;
+ break;
+ }
+ if (mcap_mdl->GetResponseCode() >= 0) {
+ LOG(ERROR) << "MDL already got response " << mcap_mdl->GetResponseCode()
+ << " for id " << p_data->reconnect_cfm.mdl_id;
+ break;
+ }
+ mcap_mdl->SetResponseCode(p_data->reconnect_cfm.rsp_code);
+ break;
+
+ case MCA_ABORT_IND_EVT:
+ print_mcap_event(&p_data->abort_ind);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+ break;
+ }
+ if (!mcap_mcl->IsConnected()) {
+ LOG(ERROR) << "MCL not connected for mcl_handle " << (int)mcl;
+ break;
+ }
+ mcap_mdl = mcap_mcl->FindMdlById(p_data->abort_ind.mdl_id);
+ if (!mcap_mdl) {
+ LOG(ERROR) << "MDL not found for id " << (int)p_data->abort_ind.mdl_id;
+ break;
+ }
+ if (mcap_mdl->IsConnected()) {
+ LOG(ERROR) << "MDL is already connected for id "
+ << (int)p_data->abort_ind.mdl_id;
+ }
+ mcap_mcl->RemoveMdl(p_data->abort_ind.mdl_id);
+ break;
+
+ case MCA_ABORT_CFM_EVT:
+ // Called when MCA_Abort succeeded
+ print_mcap_event(&p_data->abort_cfm);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+ break;
+ }
+ if (!mcap_mcl->IsConnected()) {
+ LOG(ERROR) << "MCL " << (int)mcl << " not connected";
+ break;
+ }
+ mcap_mdl = mcap_mcl->FindMdlById(p_data->abort_cfm.mdl_id);
+ if (!mcap_mdl) {
+ LOG(ERROR) << "MDL not found for id " << (int)p_data->abort_cfm.mdl_id;
+ break;
+ }
+ if (mcap_mdl->IsConnected()) {
+ LOG(ERROR) << "MDL is already connected for id "
+ << (int)p_data->abort_cfm.mdl_id;
+ }
+ mcap_mcl->RemoveMdl(p_data->abort_cfm.mdl_id);
+ break;
+
+ case MCA_DELETE_IND_EVT:
+ print_mcap_event(&p_data->delete_ind);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+ break;
+ }
+ if (!mcap_mcl->IsConnected()) {
+ LOG(ERROR) << "MCL " << (int)mcl << " not connected";
+ break;
+ }
+ if (p_data->delete_ind.mdl_id == MCA_ALL_MDL_ID) {
+ mcap_mcl->RemoveAllMdl();
+ } else {
+ mcap_mcl->RemoveMdl(p_data->delete_ind.mdl_id);
+ }
+ break;
+
+ case MCA_DELETE_CFM_EVT:
+ // Called when MCA_Delete succeeded
+ print_mcap_event(&p_data->delete_cfm);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+ break;
+ }
+ if (!mcap_mcl->IsConnected()) {
+ LOG(ERROR) << "MCL " << (int)mcl << " not connected";
+ break;
+ }
+ if (p_data->delete_cfm.rsp_code) {
+ LOG(ERROR) << "No success response " << (int)p_data->delete_cfm.rsp_code
+ << " when deleting MDL_ID "
+ << (int)p_data->delete_cfm.mdl_id;
+ break;
+ }
+ if (p_data->delete_cfm.mdl_id == MCA_ALL_MDL_ID) {
+ mcap_mcl->RemoveAllMdl();
+ } else {
+ mcap_mcl->RemoveMdl(p_data->delete_cfm.mdl_id);
+ }
+ break;
+
+ case MCA_CONNECT_IND_EVT: {
+ // Called when MCA_ConnectReq succeeded
+ print_mcap_event(&p_data->connect_ind);
+ LOG(INFO) << "Received MCL handle " << (int)mcl;
+ bt_bdaddr_t bd_addr = *((bt_bdaddr_t*)p_data->connect_ind.bd_addr);
+ mcap_mcl = FindMclByPeerAddress(bd_addr);
+ if (!mcap_mcl) {
+ LOG(INFO) << "Creating new MCL for ID " << (int)mcl;
+ _mcl_list.push_back(
+ McapMcl(_mcap_test_interface, _mcap_handle, bd_addr));
+ mcap_mcl = &_mcl_list[_mcl_list.size() - 1];
+ }
+ if (mcap_mcl->IsConnected()) {
+ LOG(ERROR) << "MCL is already connected for handle " << (int)mcl;
+ break;
+ }
+ mcap_mcl->SetHandle(mcl);
+ mcap_mcl->SetMtu(p_data->connect_ind.mtu);
+ break;
+ }
+ case MCA_DISCONNECT_IND_EVT: {
+ // Called when MCA_ConnectReq failed or MCA_DisconnectReq succeeded
+ print_mcap_event(&p_data->disconnect_ind);
+ bt_bdaddr_t bd_addr = *((bt_bdaddr_t*)p_data->disconnect_ind.bd_addr);
+ mcap_mcl = FindMclByPeerAddress(bd_addr);
+ if (!mcap_mcl) {
+ bdstr_t bd_addr_str;
+ LOG(ERROR) << "No MCL for BD addr "
+ << bdaddr_to_string(&bd_addr, bd_addr_str,
+ sizeof(bd_addr_str));
+ break;
+ }
+ if (!mcap_mcl->IsConnected()) {
+ bdstr_t bd_addr_str;
+ LOG(WARNING) << "MCL for " << bdaddr_to_string(&bd_addr, bd_addr_str,
+ sizeof(bd_addr_str))
+ << " is already disconnected";
+ }
+ mcap_mcl->SetHandle(0);
+ mcap_mcl->SetMtu(0);
+ mcap_mcl->ResetAllMdl();
+ break;
+ }
+ case MCA_OPEN_IND_EVT:
+ // Called when MCA_CreateMdlRsp succeeded step 2, data channel is open
+ // Called when MCA_ReconnectMdlRsp succeeded step 2, data channel is open
+ case MCA_OPEN_CFM_EVT:
+ // Called when MCA_CreateMdl succeeded step 2, data channel is open
+ // Called when MCA_ReconnectMdl succeeded step 2, data channel is open
+ // Called when MCA_DataChnlCfg succeeded
+ print_mcap_event(&p_data->open_ind);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+ break;
+ }
+ if (!mcap_mcl->IsConnected()) {
+ LOG(ERROR) << "MCL not connected for mcl_handle " << (int)mcl;
+ break;
+ }
+ mcap_mdl = mcap_mcl->FindMdlById(p_data->open_ind.mdl_id);
+ if (mcap_mdl) {
+ if (mcap_mdl->IsConnected()) {
+ LOG(ERROR) << "MDL is already connected for mcl_handle "
+ << (int)p_data->open_ind.mdl_id;
+ break;
+ }
+ mcap_mdl->SetMtu(p_data->open_ind.mtu);
+ mcap_mdl->SetHandle(p_data->open_ind.mdl);
+ } else {
+ LOG(ERROR) << "No MDL for mdl_id " << (int)p_data->reconnect_ind.mdl_id;
+ }
+ break;
+
+ case MCA_CLOSE_IND_EVT:
+ case MCA_CLOSE_CFM_EVT:
+ // Called when MCA_CloseReq is successful
+ print_mcap_event(&p_data->close_cfm);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+ break;
+ }
+ if (!mcap_mcl->IsConnected()) {
+ LOG(ERROR) << "MCL not connected for mcl_handle " << (int)mcl;
+ break;
+ }
+ mcap_mdl = mcap_mcl->FindMdlById(p_data->close_cfm.mdl_id);
+ if (mcap_mdl) {
+ mcap_mdl->SetMtu(0);
+ mcap_mdl->SetHandle(0);
+ } else {
+ LOG(WARNING) << "No MDL for mdl_id " << (int)p_data->close_cfm.mdl_id;
+ }
+ break;
+
+ case MCA_CONG_CHG_EVT:
+ print_mcap_event(&p_data->cong_chg);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+ break;
+ }
+ if (!mcap_mcl->IsConnected()) {
+ LOG(ERROR) << "MCL not connected for mcl_handle " << (int)mcl;
+ break;
+ }
+ break;
+
+ case MCA_RSP_TOUT_IND_EVT:
+ case MCA_SYNC_CAP_IND_EVT:
+ case MCA_SYNC_CAP_CFM_EVT:
+ case MCA_SYNC_SET_IND_EVT:
+ case MCA_SYNC_SET_CFM_EVT:
+ case MCA_SYNC_INFO_IND_EVT:
+ print_mcap_event(&p_data->hdr);
+ break;
+ }
+}
+
+} // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_app.h b/tools/mcap_tool/mcap_test_app.h
new file mode 100644
index 0000000..23dfa37
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_app.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <base/logging.h>
+
+#include "bdaddr.h"
+#include "mca_api.h"
+
+#include "mcap_test_mcl.h"
+#include "mcap_test_mdep.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+extern const tMCA_CHNL_CFG MCAP_TEST_CHANNEL_CONFIG;
+
+class McapTestApp {
+ public:
+ /**
+ * McapTestApp is the root node for an MCAP application
+ *
+ * @param mcap_test_interface interface to MCAP APIs on Bluetooth stack
+ * from get_test_interface()
+ */
+ McapTestApp(btmcap_test_interface_t* mcap_test_interface);
+ btmcap_test_interface_t* GetInterface();
+ /**
+ * Register an application with the Bluetooth stack
+ * @param ctrl_psm Control channel L2CAP PSM
+ * @param data_psm Data channel L2CAP PSM
+ * @param sec_mask Security Mask
+ * @param callback Control channel callback
+ * @return
+ */
+ bool Register(uint16_t ctrl_psm, uint16_t data_psm, uint16_t sec_mask,
+ tMCA_CTRL_CBACK* callback);
+ /**
+ * De-register the current application
+ */
+ void Deregister();
+ /**
+ * Check if current application is registered
+ * @return True if registered
+ */
+ bool Registered();
+ /**
+ * Create MCAP Communication Link
+ * @param bd_addr Peer Bluetooth Address
+ * @param ctrl_psm Control channel L2CAP PSM, should be the same as registered
+ * value for most cases
+ * @param sec_mask Security mask
+ * @return True on success
+ */
+ bool ConnectMcl(const bt_bdaddr_t& bd_addr, uint16_t ctrl_psm,
+ uint16_t sec_mask);
+ /**
+ * Create MCAP Data End Point
+ * @param type 0 - MCA_TDEP_ECHO, 1 - MCA_TDEP_DATA
+ * @param max_mdl Maximum number of data channels for this end point
+ * @param data_callback Data callback
+ * @return True on success
+ */
+ bool CreateMdep(uint8_t type, uint8_t max_mdl,
+ tMCA_DATA_CBACK* data_callback);
+ // Simple methods that are self-explanatory
+ uint8_t GetHandle();
+ McapMcl* FindMclByPeerAddress(const bt_bdaddr_t& bd_addr);
+ McapMcl* FindMclByHandle(tMCA_CL mcl_handle);
+ McapMdep* FindMdepByHandle(tMCA_DEP mdep_handle);
+ void RemoveMclByHandle(tMCA_CL mcl_handle);
+ bool IsRegistered();
+ /**
+ * Callback function for control channel, need to be called by an external
+ * function registered during McapTestApp::Register()
+ * @param handle MCAP application handle, should be the same as GetHandle()
+ * @param mcl MCL handle, FindMclByHandle(mcl) should return non-null value
+ * @param event Control event
+ * @param p_data Control data
+ */
+ void ControlCallback(tMCA_HANDLE handle, tMCA_CL mcl, uint8_t event,
+ tMCA_CTRL* p_data);
+
+ private:
+ // Initialized during start up
+ tMCA_REG _mca_reg;
+ btmcap_test_interface_t* _mcap_test_interface = nullptr;
+ std::vector<McapMcl> _mcl_list;
+ std::vector<McapMdep> _mdep_list;
+
+ // Initialized later
+ tMCA_HANDLE _mcap_handle = 0;
+};
+
+} // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_mcl.cc b/tools/mcap_tool/mcap_test_mcl.cc
new file mode 100644
index 0000000..8842c04
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_mcl.cc
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <cstring>
+
+#include <base/logging.h>
+
+#include "bdaddr.h"
+#include "mca_api.h"
+#include "mca_defs.h"
+#include "mcap_test_mcl.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+McapMcl::McapMcl(btmcap_test_interface_t* mcap_test_interface,
+ tMCA_HANDLE mcap_handle, const bt_bdaddr_t& peer_bd_addr)
+ : _mdl_list() {
+ _mcap_handle = mcap_handle;
+ _mcap_test_interface = mcap_test_interface;
+ memcpy(_peer_bd_addr.address, peer_bd_addr.address,
+ sizeof(_peer_bd_addr.address));
+}
+
+bool McapMcl::Connect(uint16_t ctrl_psm, uint16_t sec_mask) {
+ tMCA_RESULT ret = _mcap_test_interface->connect_mcl(
+ _mcap_handle, _peer_bd_addr.address, ctrl_psm, sec_mask);
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+ return ret == MCA_SUCCESS;
+}
+
+bool McapMcl::Disconnect() {
+ if (!IsConnected()) {
+ LOG(ERROR) << "MCL is not connected";
+ return false;
+ }
+ tMCA_RESULT ret = _mcap_test_interface->disconnect_mcl(_mcl_handle);
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+ return ret == MCA_SUCCESS;
+}
+
+McapMdl* McapMcl::AllocateMdl(tMCA_DEP mdep_handle, uint16_t mdl_id,
+ uint8_t dep_id, uint8_t cfg) {
+ if (!IsConnected()) {
+ LOG(ERROR) << "MCL is not connected";
+ return nullptr;
+ }
+ if (FindMdlById(mdl_id) != nullptr) {
+ LOG(ERROR) << "mdl_id=" << mdl_id << "already exists";
+ return nullptr;
+ }
+ if (!HasAvailableMdl()) {
+ LOG(ERROR) << "No more avaible MDL, currently " << _mdl_list.size();
+ return nullptr;
+ }
+ _mdl_list.push_back(McapMdl(_mcap_test_interface, _mcl_handle, mdep_handle,
+ mdl_id, dep_id, cfg));
+ return &_mdl_list[_mdl_list.size() - 1];
+}
+
+bool McapMcl::CreateMdl(tMCA_DEP mdep_handle, uint16_t data_psm,
+ uint16_t mdl_id, uint8_t peer_dep_id, uint8_t cfg,
+ bool should_connect) {
+ if (!IsConnected()) {
+ LOG(ERROR) << "MCL is not connected";
+ return false;
+ }
+ McapMdl* mcap_mdl = FindMdlById(mdl_id);
+ if (!mcap_mdl) {
+ LOG(INFO) << "mdl_id=" << mdl_id << "does not exists, creating new one";
+ mcap_mdl = AllocateMdl(mdep_handle, mdl_id, peer_dep_id, cfg);
+ if (!mcap_mdl) {
+ return false;
+ }
+ }
+ if (mcap_mdl->IsConnected()) {
+ LOG(ERROR) << "mdl_id=" << mdl_id << "is already connected with handle "
+ << (int)mcap_mdl->GetHandle();
+ return false;
+ }
+ return mcap_mdl->Create(data_psm, should_connect);
+}
+
+bool McapMcl::DataChannelConfig() {
+ tMCA_RESULT ret = _mcap_test_interface->data_channel_config(
+ _mcl_handle, get_test_channel_config());
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+ return ret == MCA_SUCCESS;
+}
+
+bool McapMcl::CreateMdlResponse(tMCA_DEP mdep_handle, uint16_t mdl_id,
+ uint8_t my_dep_id, uint8_t cfg) {
+ if (!IsConnected()) {
+ LOG(ERROR) << "MCL is not connected";
+ return false;
+ }
+ McapMdl* mcap_mdl = FindMdlById(mdl_id);
+ if (!mcap_mdl) {
+ LOG(INFO) << "mdl_id=" << mdl_id << " does not exists, creating new one";
+ mcap_mdl = AllocateMdl(mdep_handle, mdl_id, my_dep_id, cfg);
+ if (!mcap_mdl) {
+ LOG(ERROR) << "MDL cannot be created";
+ return false;
+ }
+ }
+ if (mcap_mdl->IsConnected()) {
+ LOG(INFO) << "mdl_id=" << mdl_id << " is already connected with handle "
+ << (int)mcap_mdl->GetHandle() << ", updating context";
+ mcap_mdl->UpdateContext(mdep_handle, my_dep_id, cfg);
+ }
+ return mcap_mdl->CreateResponse();
+}
+
+bool McapMcl::AbortMdl() {
+ tMCA_RESULT ret = _mcap_test_interface->abort_mdl(_mcl_handle);
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+ return ret == MCA_SUCCESS;
+}
+
+bool McapMcl::DeleteMdl(uint16_t mdl_id) {
+ tMCA_RESULT ret = _mcap_test_interface->delete_mdl(_mcl_handle, mdl_id);
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+ return ret == MCA_SUCCESS;
+}
+
+bt_bdaddr_t& McapMcl::GetPeerAddress() { return _peer_bd_addr; }
+
+void McapMcl::SetHandle(tMCA_CL handle) { _mcl_handle = handle; }
+
+tMCA_CL McapMcl::GetHandle() const { return _mcl_handle; }
+
+void McapMcl::SetMtu(uint16_t mtu) { _control_mtu = mtu; }
+
+uint16_t McapMcl::GetMtu() { return _control_mtu; }
+
+McapMdl* McapMcl::FindMdlById(uint16_t mdl_id) {
+ for (McapMdl& mdl : _mdl_list) {
+ if (mdl.GetId() == mdl_id) {
+ return &mdl;
+ }
+ }
+ return nullptr;
+}
+
+McapMdl* McapMcl::FindMdlByHandle(tMCA_DL mdl_handle) {
+ for (McapMdl& mdl : _mdl_list) {
+ if (mdl.GetHandle() == mdl_handle) {
+ return &mdl;
+ }
+ }
+ return nullptr;
+}
+
+void McapMcl::RemoveAllMdl() { _mdl_list.clear(); }
+
+void McapMcl::RemoveMdl(uint16_t mdl_id) {
+ LOG(INFO) << "Removing MDL id " << (int)mdl_id;
+ for (std::vector<McapMdl>::iterator it = _mdl_list.begin();
+ it != _mdl_list.end(); ++it) {
+ if (it->GetId() == mdl_id) {
+ _mdl_list.erase(it);
+ LOG(INFO) << "Removed MDL id " << (int)mdl_id;
+ return;
+ }
+ }
+}
+
+void McapMcl::ResetAllMdl() {
+ for (McapMdl& mcap_mdl : _mdl_list) {
+ mcap_mdl.SetHandle(0);
+ mcap_mdl.SetMtu(0);
+ mcap_mdl.SetResponseCode(-1);
+ }
+}
+
+void McapMcl::ResetMdl(uint16_t mdl_id) {
+ LOG(INFO) << "Closing MDL id " << (int)mdl_id;
+ McapMdl* mcap_mdl = FindMdlById(mdl_id);
+ if (!mcap_mdl) {
+ LOG(ERROR) << "Cannot find MDL for id " << (int)mdl_id;
+ return;
+ }
+ if (mcap_mdl->IsConnected()) {
+ LOG(ERROR) << "MDL " << (int)mdl_id << " is still connected";
+ return;
+ }
+ mcap_mdl->SetHandle(0);
+ mcap_mdl->SetMtu(0);
+ mcap_mdl->SetResponseCode(-1);
+}
+
+bool McapMcl::IsConnected() { return _mcl_handle > 0; }
+
+int McapMcl::ConnectedMdlCount() {
+ int count = 0;
+ for (McapMdl& mcap_mdl : _mdl_list) {
+ if (mcap_mdl.IsConnected()) {
+ count++;
+ }
+ }
+ return count;
+}
+
+bool McapMcl::HasAvailableMdl() { return ConnectedMdlCount() < MCA_NUM_MDLS; }
+
+} // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_mcl.h b/tools/mcap_tool/mcap_test_mcl.h
new file mode 100644
index 0000000..2d71ee6
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_mcl.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <vector>
+
+#include "bdaddr.h"
+#include "mca_api.h"
+#include "mcap_test_mdl.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+class McapMcl {
+ public:
+ /**
+ * A controller for a MCAP Communication Link (MCL)
+ * @param mcap_test_interface Underlining interface to Bluetooth stack
+ * @param mcap_handle Parent application handle
+ * @param peer_bd_addr Peer Bluetooth MAC address
+ */
+ McapMcl(btmcap_test_interface_t* mcap_test_interface, tMCA_HANDLE mcap_handle,
+ const bt_bdaddr_t& peer_bd_addr);
+ /**
+ * Connect this MCL's control channel
+ * @param ctrl_psm Control channel L2CAP PSM
+ * @param sec_mask Security mask
+ * @return True on success
+ */
+ bool Connect(uint16_t ctrl_psm, uint16_t sec_mask);
+ /**
+ * Disconnect from control channel
+ * @return
+ */
+ bool Disconnect();
+ /**
+ * Close this MCL connection
+ * @return
+ */
+ bool Close();
+ /**
+ * Allocate an MCAP Data Link (MDL) object and save to this MCL object
+ * @param mdep_handle MDEP handle for data, MDEP must be prior created
+ * @param mdl_id Desired MDL ID, user supported
+ * @param dep_id Peer MDEP ID
+ * @param cfg Configuration flags
+ * @return True on success
+ */
+ McapMdl* AllocateMdl(tMCA_DEP mdep_handle, uint16_t mdl_id, uint8_t dep_id,
+ uint8_t cfg);
+ /**
+ * Send CREATE_MDL message to peer device, will allocate an MDL if the MDL for
+ * corresponding MDL ID was not allocated before
+ * @param mdep_handle MDEP handle for data, MDEP must be prior created
+ * @param data_psm Data channel L2CAP PSM
+ * @param mdl_id Desired MDL ID, user supported
+ * @param peer_dep_id Peer MDEP ID
+ * @param cfg Configuration flags
+ * @param should_connect whether we should connect L2CAP immediately
+ * @return True on success
+ */
+ bool CreateMdl(tMCA_DEP mdep_handle, uint16_t data_psm, uint16_t mdl_id,
+ uint8_t peer_dep_id, uint8_t cfg, bool should_connect);
+ /**
+ * Configure data channel, unblock any pending MDL L2CAP connection requests
+ * @return True on Success
+ */
+ bool DataChannelConfig();
+ /**
+ * Respond to CREATE_MDL message received from peer device, will allocate an
+ * MDL if the MDL for corresponding MDL ID was not allocated before
+ * @param mdep_handle MDEP handle for data, MDEP must be prior created
+ * @param mdl_id Desired MDL ID, peer supported
+ * @param my_dep_id My MDEP ID
+ * @param cfg Configuration flags
+ * @return True on success
+ */
+ bool CreateMdlResponse(tMCA_DEP mdep_handle, uint16_t mdl_id,
+ uint8_t my_dep_id, uint8_t cfg);
+ /**
+ * Send ABORT_MDL request, aborting all pending CREATE_MDL requests
+ * @return
+ */
+ bool AbortMdl();
+ /**
+ * Send DELETE_MDL request to remote
+ * @param mdl_id None zero value mdl_id, 0xFFFF for all remote MDLs
+ * @return True on success
+ */
+ bool DeleteMdl(uint16_t mdl_id);
+ // Simple methods that are self-explanatory
+ bt_bdaddr_t& GetPeerAddress();
+ void SetHandle(tMCA_CL handle);
+ tMCA_CL GetHandle() const;
+ void SetMtu(uint16_t mtu);
+ uint16_t GetMtu();
+ McapMdl* FindMdlById(uint16_t mdl_id);
+ McapMdl* FindMdlByHandle(tMCA_DL mdl_handle);
+ void RemoveAllMdl();
+ void RemoveMdl(uint16_t mdl_id);
+ void ResetAllMdl();
+ void ResetMdl(uint16_t mdl_id);
+ bool IsConnected();
+ int ConnectedMdlCount();
+ bool HasAvailableMdl();
+
+ private:
+ // Initialized during start up
+ btmcap_test_interface_t* _mcap_test_interface;
+ tMCA_HANDLE _mcap_handle;
+ bt_bdaddr_t _peer_bd_addr;
+ std::vector<McapMdl> _mdl_list;
+
+ // Initialized later
+ tMCA_CL _mcl_handle = 0;
+ uint16_t _control_mtu = 0;
+};
+
+} // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_mdep.cc b/tools/mcap_tool/mcap_test_mdep.cc
new file mode 100644
index 0000000..1fb6412
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_mdep.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <base/logging.h>
+
+#include "mcap_test_mdep.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+McapMdep::McapMdep(btmcap_test_interface_t* mcap_test_interface,
+ tMCA_HANDLE mcap_handle, uint8_t type, uint8_t max_mdl,
+ tMCA_DATA_CBACK* data_callback) {
+ _mcap_test_interface = mcap_test_interface;
+ _mcap_handle = mcap_handle;
+ _mca_cs.type = (0 == type) ? MCA_TDEP_ECHO : MCA_TDEP_DATA;
+ _mca_cs.max_mdl = max_mdl;
+ _mca_cs.p_data_cback = data_callback;
+}
+
+bool McapMdep::Create() {
+ tMCA_RESULT ret =
+ _mcap_test_interface->create_mdep(_mcap_handle, &_mdep_handle, &_mca_cs);
+ LOG(INFO) << "mdep_handle=" << (int)_mdep_handle;
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+ return ret == MCA_SUCCESS;
+}
+
+bool McapMdep::Delete() {
+ tMCA_RESULT ret =
+ _mcap_test_interface->create_mdep(_mcap_handle, &_mdep_handle, &_mca_cs);
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+ return ret == MCA_SUCCESS;
+}
+
+tMCA_DEP McapMdep::GetHandle() const { return _mdep_handle; }
+
+bool McapMdep::IsRegistered() { return _mdep_handle > 0; }
+
+} // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_mdep.h b/tools/mcap_tool/mcap_test_mdep.h
new file mode 100644
index 0000000..a852d52
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_mdep.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "mca_api.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+class McapMdep {
+ public:
+ /**
+ * A control abstraction for a MCAP Data End Point (MDEP)
+ * @param mcap_test_interface Underlining MCAP interface to Bluetooth stack
+ * @param mcap_handle Parent MCAP application handle
+ * @param type 0 for Echo, 1 for Normal, nothing else
+ * @param max_mdl Maximum number of MDL's allowed
+ * @param data_callback Data channel callback function
+ */
+ McapMdep(btmcap_test_interface_t* mcap_test_interface,
+ tMCA_HANDLE mcap_handle, uint8_t type, uint8_t max_mdl,
+ tMCA_DATA_CBACK* data_callback);
+ /**
+ * Actually create the MDEP in the stack
+ * @return True if success
+ */
+ bool Create();
+ /**
+ * Destroy the MDEP in the stack
+ * @return True if success
+ */
+ bool Delete();
+ // Simple methods that are self-explanatory
+ tMCA_DEP GetHandle() const;
+ bool IsRegistered();
+
+ private:
+ // Initialized during start up
+ btmcap_test_interface_t* _mcap_test_interface;
+ tMCA_HANDLE _mcap_handle;
+ tMCA_CS _mca_cs;
+
+ // Initialized later
+ tMCA_DEP _mdep_handle = 0;
+};
+
+} // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_mdl.cc b/tools/mcap_tool/mcap_test_mdl.cc
new file mode 100644
index 0000000..e7b8083
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_mdl.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <base/logging.h>
+
+#include "mca_defs.h"
+#include "mcap_test_mdl.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+/* Test MCAP Channel Configurations */
+const tMCA_CHNL_CFG MCAP_TEST_CHANNEL_CONFIG = {
+ .fcr_opt =
+ {
+ L2CAP_FCR_ERTM_MODE,
+ MCA_FCR_OPT_TX_WINDOW_SIZE, /* Tx window size */
+ /* Maximum transmissions before disconnecting */
+ MCA_FCR_OPT_MAX_TX_B4_DISCNT,
+ MCA_FCR_OPT_RETX_TOUT, /* retransmission timeout (2 secs) */
+ MCA_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */
+ MCA_FCR_OPT_MPS_SIZE, /* MPS segment size */
+ },
+ .user_rx_buf_size = BT_DEFAULT_BUFFER_SIZE,
+ .user_tx_buf_size = BT_DEFAULT_BUFFER_SIZE,
+ .fcr_rx_buf_size = BT_DEFAULT_BUFFER_SIZE,
+ .fcr_tx_buf_size = BT_DEFAULT_BUFFER_SIZE,
+ .fcs = MCA_FCS_NONE,
+ .data_mtu = 572 /* L2CAP MTU of the MCAP data channel */
+};
+
+const tMCA_CHNL_CFG* get_test_channel_config() {
+ return &MCAP_TEST_CHANNEL_CONFIG;
+}
+
+McapMdl::McapMdl(btmcap_test_interface_t* mcap_test_interface,
+ tMCA_CL mcl_handle, tMCA_DEP mdep_handle, uint16_t mdl_id,
+ uint8_t dep_id, uint8_t cfg) {
+ _mcap_test_interface = mcap_test_interface;
+ _mcl_handle = mcl_handle;
+ _mdep_handle = mdep_handle;
+ _mdl_id = mdl_id;
+ _dep_id = dep_id;
+ _cfg = cfg;
+}
+
+bool McapMdl::UpdateContext(tMCA_DEP mdep_handle, uint8_t dep_id, uint8_t cfg) {
+ if (!_mdl_handle) {
+ LOG(ERROR) << "MDL handle not initialized";
+ }
+ _mdep_handle = mdep_handle;
+ _dep_id = dep_id;
+ _cfg = cfg;
+ tMCA_RESULT ret = _mcap_test_interface->close_mdl_request(_mdl_handle);
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+ if (ret != MCA_SUCCESS) return false;
+ SetHandle(0);
+ SetResponseCode(-1);
+ SetMtu(0);
+ return true;
+}
+
+bool McapMdl::Create(uint16_t data_psm, bool should_connect) {
+ tMCA_RESULT ret = _mcap_test_interface->create_mdl_request(
+ _mcl_handle, _mdep_handle, data_psm, _mdl_id, _dep_id, _cfg,
+ should_connect ? &MCAP_TEST_CHANNEL_CONFIG : nullptr);
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+ return ret == MCA_SUCCESS;
+}
+
+bool McapMdl::Close() {
+ if (!_mdl_handle) {
+ LOG(ERROR) << "MDL handle not initialized";
+ return false;
+ }
+ tMCA_RESULT ret = _mcap_test_interface->close_mdl_request(_mdl_handle);
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+ return ret == MCA_SUCCESS;
+}
+
+bool McapMdl::Reconnect(uint16_t data_psm) {
+ tMCA_RESULT ret = _mcap_test_interface->reconnect_mdl_request(
+ _mcl_handle, _mdep_handle, data_psm, _mdl_id, &MCAP_TEST_CHANNEL_CONFIG);
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+ return ret == MCA_SUCCESS;
+}
+
+bool McapMdl::ReconnectResponse() {
+ tMCA_RESULT ret = _mcap_test_interface->reconnect_mdl_response(
+ _mcl_handle, _mdep_handle, _mdl_id, MCA_RSP_SUCCESS,
+ &MCAP_TEST_CHANNEL_CONFIG);
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+ return ret == MCA_SUCCESS;
+}
+
+bool McapMdl::CreateResponse() {
+ tMCA_RESULT ret = _mcap_test_interface->create_mdl_response(
+ _mcl_handle, _dep_id, _mdl_id, _cfg, MCA_SUCCESS,
+ &MCAP_TEST_CHANNEL_CONFIG);
+ LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+ return ret == MCA_SUCCESS;
+}
+
+bool McapMdl::IsConnected() { return _mdl_handle > 0; }
+
+uint16_t McapMdl::GetId() { return _mdl_id; }
+
+int32_t McapMdl::GetResponseCode() { return _mdl_rsp_code; }
+
+void McapMdl::SetResponseCode(int32_t rsp_code) { _mdl_rsp_code = rsp_code; }
+
+void McapMdl::SetHandle(tMCA_DL mdl_handle) { _mdl_handle = mdl_handle; }
+
+tMCA_DL McapMdl::GetHandle() { return _mdl_handle; }
+
+void McapMdl::SetMtu(uint16_t mtu) { _data_mtu = mtu; }
+
+uint16_t McapMdl::GetMtu() { return _data_mtu; }
+
+} // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_mdl.h b/tools/mcap_tool/mcap_test_mdl.h
new file mode 100644
index 0000000..7dadc34
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_mdl.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "mca_api.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+const tMCA_CHNL_CFG* get_test_channel_config();
+
+class McapMdl {
+ public:
+ /**
+ * An abstraction for the MCAP Data Link (MDL)
+ * @param mcap_test_interface Underlining MCAP interface to Bluetooth stack
+ * @param mcl_handle Parent MCL handle
+ * @param mdep_handle Associated MDEP handle
+ * @param mdl_id Desired MDL ID, application supported
+ * @param dep_id Peer or self MDEP ID
+ * @param cfg Configuration flags
+ */
+ McapMdl(btmcap_test_interface_t* mcap_test_interface, tMCA_CL mcl_handle,
+ tMCA_DEP mdep_handle, uint16_t mdl_id, uint8_t dep_id, uint8_t cfg);
+ /**
+ * Update this MDL's context so that it can be reused for a new connection
+ * This will close this MDL connection at the same time
+ * @param mdep_handle Associated MDEP handle
+ * @param dep_id Peer or self MDEP ID
+ * @param cfg Configuration flags
+ * @return True on success
+ */
+ bool UpdateContext(tMCA_DEP mdep_handle, uint8_t dep_id, uint8_t cfg);
+ /**
+ * Request to create this MDL to remote device through MCL
+ * The create command won't initiate an L2CAP connection unless a non-null
+ * config is given
+ * @param data_psm Data channel L2CAP PSM
+ * @return True on success
+ */
+ bool Create(uint16_t data_psm, bool should_connect);
+ /**
+ * Connect this MDL to remote by configuring the data channel
+ * @return True on success
+ */
+ bool Connect();
+ /**
+ * Close this MDL connection
+ * @return True on success
+ */
+ bool Close();
+ /**
+ * Request to reconnect connect this MDL to remote device through MCL
+ * @param data_psm Data channel L2CAP PSM
+ * @return True on success
+ */
+ bool Reconnect(uint16_t data_psm);
+ /**
+ * Respond to a reconnect request from peer
+ * @return True on success
+ */
+ bool ReconnectResponse();
+ /**
+ * Respond to a connect request from peer
+ * @return True on success
+ */
+ bool CreateResponse();
+ bool IsConnected();
+ int32_t GetResponseCode();
+ void SetResponseCode(int32_t rsp_code);
+ uint16_t GetId();
+ void SetHandle(tMCA_DL mdl_handle);
+ tMCA_DL GetHandle();
+ void SetMtu(uint16_t mtu);
+ uint16_t GetMtu();
+
+ private:
+ // Initialized at start up
+ btmcap_test_interface_t* _mcap_test_interface;
+ tMCA_CL _mcl_handle;
+ tMCA_DEP _mdep_handle;
+ uint16_t _mdl_id;
+ uint8_t _dep_id;
+ uint8_t _cfg;
+
+ // Initialized later
+ tMCA_DL _mdl_handle = 0;
+ uint16_t _data_mtu = 0;
+ int32_t _mdl_rsp_code = -1;
+};
+
+} // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_tool.cc b/tools/mcap_tool/mcap_tool.cc
new file mode 100644
index 0000000..e84c35e
--- /dev/null
+++ b/tools/mcap_tool/mcap_tool.cc
@@ -0,0 +1,973 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2015, The linux Foundation. All rights reserved.
+ *
+ * Not a Contribution.
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/************************************************************************************
+ *
+ * Filename: mcap_tool.cc
+ *
+ * Description: Fluoride MCAP Test Tool application
+ *
+ ***********************************************************************************/
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/hardware.h>
+#ifndef OS_GENERIC
+#include <private/android_filesystem_config.h>
+#endif
+#include <base/logging.h>
+
+#include "bdaddr.h"
+#include "bt_types.h"
+#include "l2c_api.h"
+#include "mca_api.h"
+#include "mca_defs.h"
+#include "osi/include/compat.h"
+#if defined(OS_GENERIC)
+#include "hal_util.h"
+#endif
+#include "mcap_test_app.h"
+#include "mcap_test_mcl.h"
+#include "mcap_test_mdep.h"
+#include "mcap_test_mdl.h"
+
+using SYSTEM_BT_TOOLS_MCAP_TOOL::McapTestApp;
+using SYSTEM_BT_TOOLS_MCAP_TOOL::McapMcl;
+using SYSTEM_BT_TOOLS_MCAP_TOOL::McapMdep;
+using SYSTEM_BT_TOOLS_MCAP_TOOL::McapMdl;
+
+/******************************************************************************
+ * Constants & Macros
+ *****************************************************************************/
+#define PID_FILE "/data/.bdt_pid"
+
+#ifndef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#define CASE_RETURN_STR(const) \
+ case const: \
+ return #const;
+
+#ifndef OS_GENERIC
+/* Permission Groups */
+static gid_t groups[] = {AID_NET_BT, AID_INET, AID_NET_BT_ADMIN,
+ AID_SYSTEM, AID_MISC, AID_SDCARD_RW,
+ AID_NET_ADMIN, AID_VPN};
+#endif
+/******************************************************************************
+ * Static variables
+ *****************************************************************************/
+/* Console loop states */
+static bool global_main_done = false;
+static bt_status_t global_status;
+static bool global_strict_mode = false;
+
+/* Device and Profile Interfaces */
+static bluetooth_device_t* sBtDevice = nullptr;
+const bt_interface_t* sBtInterface = nullptr;
+static btmcap_test_interface_t* sMcapTestInterface = nullptr;
+static McapTestApp* sMcapTestApp = nullptr;
+
+/* Bluetooth stack states */
+static bool global_bt_enabled = false;
+static int global_adapter_state = BT_STATE_OFF;
+static int global_pair_state = BT_BOND_STATE_NONE;
+/************************************************************************************
+** Static functions
+************************************************************************************/
+static void process_cmd(char* p, bool is_job);
+
+/*******************************************************************************
+ ** Misc helper functions
+ *******************************************************************************/
+static const char* dump_bt_status(const bt_status_t status) {
+ switch (status) {
+ CASE_RETURN_STR(BT_STATUS_SUCCESS)
+ CASE_RETURN_STR(BT_STATUS_FAIL)
+ CASE_RETURN_STR(BT_STATUS_NOT_READY)
+ CASE_RETURN_STR(BT_STATUS_NOMEM)
+ CASE_RETURN_STR(BT_STATUS_BUSY)
+ CASE_RETURN_STR(BT_STATUS_UNSUPPORTED)
+ default:
+ return "unknown status code";
+ }
+}
+
+/************************************************************************************
+** MCAP Callbacks
+************************************************************************************/
+static void mcap_ctrl_callback(tMCA_HANDLE handle, tMCA_CL mcl, uint8_t event,
+ tMCA_CTRL* p_data) {
+ sMcapTestApp->ControlCallback(handle, mcl, event, p_data);
+}
+
+static void mcap_data_cb(tMCA_DL mdl, BT_HDR* p_pkt) {
+ printf("%s: mdl=%d, event=%d, len=%d, offset=%d, layer_specific=%d\n",
+ __func__, mdl, p_pkt->event, p_pkt->len, p_pkt->offset,
+ p_pkt->layer_specific);
+ printf("%s: HEXDUMP OF DATA LENGTH %u:\n", __func__, p_pkt->len);
+ printf("=========================Begin=========================\n");
+ bool newline = false;
+ for (int i = 0; i < p_pkt->len; ++i) {
+ printf("%02x", p_pkt->data[i]);
+ if (i > 0 && (i % 25) == 0) {
+ printf("\n");
+ newline = true;
+ } else {
+ printf(" ");
+ newline = false;
+ }
+ }
+ if (!newline) printf("\n");
+ printf("=========================End===========================\n");
+}
+
+/************************************************************************************
+** Shutdown helper functions
+************************************************************************************/
+
+static void console_shutdown(void) {
+ LOG(INFO) << __func__ << ": Shutdown Fluoride MCAP test app";
+ global_main_done = true;
+}
+
+/*****************************************************************************
+** Android's init.rc does not yet support applying linux capabilities
+*****************************************************************************/
+
+#ifndef OS_GENERIC
+static void config_permissions(void) {
+ struct __user_cap_header_struct header;
+ struct __user_cap_data_struct cap[2];
+
+ printf("set_aid_and_cap : pid %d, uid %d gid %d", getpid(), getuid(),
+ getgid());
+
+ header.pid = 0;
+
+ prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+ setuid(AID_BLUETOOTH);
+ setgid(AID_BLUETOOTH);
+
+ header.version = _LINUX_CAPABILITY_VERSION_3;
+
+ cap[CAP_TO_INDEX(CAP_NET_RAW)].permitted |= CAP_TO_MASK(CAP_NET_RAW);
+ cap[CAP_TO_INDEX(CAP_NET_ADMIN)].permitted |= CAP_TO_MASK(CAP_NET_ADMIN);
+ cap[CAP_TO_INDEX(CAP_NET_BIND_SERVICE)].permitted |=
+ CAP_TO_MASK(CAP_NET_BIND_SERVICE);
+ cap[CAP_TO_INDEX(CAP_SYS_RAWIO)].permitted |= CAP_TO_MASK(CAP_SYS_RAWIO);
+ cap[CAP_TO_INDEX(CAP_SYS_NICE)].permitted |= CAP_TO_MASK(CAP_SYS_NICE);
+ cap[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
+ cap[CAP_TO_INDEX(CAP_WAKE_ALARM)].permitted |= CAP_TO_MASK(CAP_WAKE_ALARM);
+
+ cap[CAP_TO_INDEX(CAP_NET_RAW)].effective |= CAP_TO_MASK(CAP_NET_RAW);
+ cap[CAP_TO_INDEX(CAP_NET_ADMIN)].effective |= CAP_TO_MASK(CAP_NET_ADMIN);
+ cap[CAP_TO_INDEX(CAP_NET_BIND_SERVICE)].effective |=
+ CAP_TO_MASK(CAP_NET_BIND_SERVICE);
+ cap[CAP_TO_INDEX(CAP_SYS_RAWIO)].effective |= CAP_TO_MASK(CAP_SYS_RAWIO);
+ cap[CAP_TO_INDEX(CAP_SYS_NICE)].effective |= CAP_TO_MASK(CAP_SYS_NICE);
+ cap[CAP_TO_INDEX(CAP_SETGID)].effective |= CAP_TO_MASK(CAP_SETGID);
+ cap[CAP_TO_INDEX(CAP_WAKE_ALARM)].effective |= CAP_TO_MASK(CAP_WAKE_ALARM);
+
+ capset(&header, &cap[0]);
+ setgroups(sizeof(groups) / sizeof(groups[0]), groups);
+}
+#endif
+
+/*******************************************************************************
+ ** Console helper functions
+ *******************************************************************************/
+
+void skip_blanks(char** p) {
+ while (**p == ' ') (*p)++;
+}
+
+uint32_t get_int(char** p, int DefaultValue) {
+ uint32_t Value = 0;
+ unsigned char UseDefault;
+
+ UseDefault = 1;
+ skip_blanks(p);
+
+ while (((**p) <= '9' && (**p) >= '0')) {
+ Value = Value * 10 + (**p) - '0';
+ UseDefault = 0;
+ (*p)++;
+ }
+
+ if (UseDefault)
+ return DefaultValue;
+ else
+ return Value;
+}
+
+int get_signed_int(char** p, int DefaultValue) {
+ int Value = 0;
+ unsigned char UseDefault;
+ unsigned char NegativeNum = 0;
+
+ UseDefault = 1;
+ skip_blanks(p);
+
+ if ((**p) == '-') {
+ NegativeNum = 1;
+ (*p)++;
+ }
+ while (((**p) <= '9' && (**p) >= '0')) {
+ Value = Value * 10 + (**p) - '0';
+ UseDefault = 0;
+ (*p)++;
+ }
+
+ if (UseDefault)
+ return DefaultValue;
+ else
+ return ((NegativeNum == 0) ? Value : -Value);
+}
+
+void get_str(char** p, char* Buffer) {
+ skip_blanks(p);
+
+ while (**p != 0 && **p != ' ') {
+ *Buffer = **p;
+ (*p)++;
+ Buffer++;
+ }
+
+ *Buffer = 0;
+}
+
+uint32_t get_hex_any(char** p, int DefaultValue, unsigned int NumOfNibble) {
+ uint32_t Value = 0;
+ unsigned char UseDefault;
+ // unsigned char NumOfNibble = 8; //Since we are returning uint32, max
+ // allowed is 4 bytes(8 nibbles).
+
+ UseDefault = 1;
+ skip_blanks(p);
+
+ while ((NumOfNibble) &&
+ (((**p) <= '9' && (**p) >= '0') || ((**p) <= 'f' && (**p) >= 'a') ||
+ ((**p) <= 'F' && (**p) >= 'A'))) {
+ if (**p >= 'a')
+ Value = Value * 16 + (**p) - 'a' + 10;
+ else if (**p >= 'A')
+ Value = Value * 16 + (**p) - 'A' + 10;
+ else
+ Value = Value * 16 + (**p) - '0';
+ UseDefault = 0;
+ (*p)++;
+ NumOfNibble--;
+ }
+
+ if (UseDefault)
+ return DefaultValue;
+ else
+ return Value;
+}
+uint32_t get_hex(char** p, int DefaultValue) {
+ return get_hex_any(p, DefaultValue, 8);
+}
+uint8_t get_hex_byte(char** p, int DefaultValue) {
+ return get_hex_any(p, DefaultValue, 2);
+}
+
+bool is_cmd(const char* cmd, const char* str) {
+ return (strlen(str) == strlen(cmd)) && (strncmp(cmd, str, strlen(str)) == 0);
+}
+
+typedef void(console_cmd_handler_t)(char* p);
+
+typedef struct {
+ const char* name;
+ console_cmd_handler_t* handler;
+ const char* help;
+ bool is_job;
+} cmd_t;
+
+extern const cmd_t console_cmd_list[];
+static int console_cmd_maxlen = 0;
+
+static void* cmdjob_handler(void* param) {
+ char* job_cmd = (char*)param;
+ LOG(INFO) << "cmdjob starting: " << job_cmd;
+ process_cmd(job_cmd, true);
+ LOG(INFO) << "cmdjob terminating";
+ free(job_cmd);
+ return nullptr;
+}
+
+static int create_cmdjob(char* cmd) {
+ CHECK(cmd);
+ char* job_cmd = (char*)calloc(1, strlen(cmd) + 1); /* freed in job handler */
+ if (job_cmd) {
+ strlcpy(job_cmd, cmd, strlen(job_cmd) + 1);
+ pthread_t thread_id;
+ int ret =
+ pthread_create(&thread_id, nullptr, cmdjob_handler, (void*)job_cmd);
+ LOG_IF(ERROR, ret != 0) << "Error during pthread_create";
+ } else {
+ LOG(INFO) << "Cannot Allocate memory for cmdjob: " << cmd;
+ }
+ return 0;
+}
+
+/*******************************************************************************
+ ** Load stack lib
+ *******************************************************************************/
+
+int HAL_load(void) {
+ int err = 0;
+ hw_module_t* module;
+ hw_device_t* device;
+ LOG(INFO) << "Loading HAL library and extensions";
+#if defined(OS_GENERIC)
+ err = hal_util_load_bt_library((hw_module_t const**)&module);
+#else
+ err = hw_get_module(BT_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
+#endif
+ if (!err) {
+ err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &device);
+ if (!err) {
+ sBtDevice = (bluetooth_device_t*)device;
+ sBtInterface = sBtDevice->get_bluetooth_interface();
+ }
+ }
+ LOG(INFO) << "HAL library loaded, status: " << strerror(err);
+ return err;
+}
+
+int HAL_unload(void) {
+ int err = 0;
+ LOG(INFO) << "Unloading HAL lib";
+ sBtInterface = nullptr;
+ LOG(INFO) << "HAL library unloaded, status: " << strerror(err);
+ return err;
+}
+
+/*******************************************************************************
+ ** HAL test functions & callbacks
+ *******************************************************************************/
+
+void setup_test_env(void) {
+ int i = 0;
+ while (console_cmd_list[i].name) {
+ console_cmd_maxlen =
+ MAX(console_cmd_maxlen, (int)strlen(console_cmd_list[i].name));
+ i++;
+ }
+}
+
+void check_return_status(bt_status_t status) {
+ if (status != BT_STATUS_SUCCESS) {
+ LOG(INFO) << "HAL REQUEST FAILED status : " << status << " ("
+ << dump_bt_status(status) << ")";
+ } else {
+ LOG(INFO) << "HAL REQUEST SUCCESS";
+ }
+}
+
+static void adapter_state_changed(bt_state_t state) {
+ int V1 = 1000, V2 = 2;
+ bt_property_t property = {BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, 4, &V1};
+ bt_property_t property1 = {BT_PROPERTY_ADAPTER_SCAN_MODE, 2, &V2};
+ bt_property_t property2 = {BT_PROPERTY_BDNAME, 6, (void*)"Fluoride_Test"};
+
+ global_adapter_state = state;
+
+ if (state == BT_STATE_ON) {
+ global_bt_enabled = true;
+ global_status = (bt_status_t)sBtInterface->set_adapter_property(&property1);
+ global_status = (bt_status_t)sBtInterface->set_adapter_property(&property);
+ global_status = (bt_status_t)sBtInterface->set_adapter_property(&property2);
+ } else {
+ global_bt_enabled = false;
+ }
+}
+
+static void adapter_properties_changed(bt_status_t status, int num_properties,
+ bt_property_t* properties) {
+ bt_bdaddr_t bd_addr;
+ bdstr_t bd_addr_str;
+ if (!properties) {
+ printf("properties is null\n");
+ return;
+ }
+ switch (properties->type) {
+ case BT_PROPERTY_BDADDR:
+ memcpy(bd_addr.address, properties->val,
+ MIN((size_t)properties->len, sizeof(bd_addr)));
+ bdaddr_to_string(&bd_addr, bd_addr_str, sizeof(bd_addr_str));
+ LOG(INFO) << "Local Bd Addr = " << bd_addr_str;
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+static void discovery_state_changed(bt_discovery_state_t state) {
+ LOG(INFO) << "Discovery State Updated: "
+ << (state == BT_DISCOVERY_STOPPED ? "STOPPED" : "STARTED");
+}
+
+static void pin_request_cb(bt_bdaddr_t* remote_bd_addr, bt_bdname_t* bd_name,
+ uint32_t cod, bool min_16_digit) {
+ bt_pin_code_t pincode = {{0x31, 0x32, 0x33, 0x34}};
+
+ if (BT_STATUS_SUCCESS !=
+ sBtInterface->pin_reply(remote_bd_addr, true, 4, &pincode)) {
+ LOG(INFO) << "Pin Reply failed";
+ }
+}
+
+static void ssp_request_cb(bt_bdaddr_t* remote_bd_addr, bt_bdname_t* bd_name,
+ uint32_t cod, bt_ssp_variant_t pairing_variant,
+ uint32_t pass_key) {
+ LOG(INFO) << __func__ << ": device_name:" << bd_name->name
+ << ", pairing_variant: " << (int)pairing_variant
+ << ", passkey: " << unsigned(pass_key);
+ if (BT_STATUS_SUCCESS !=
+ sBtInterface->ssp_reply(remote_bd_addr, pairing_variant, true,
+ pass_key)) {
+ LOG(ERROR) << "SSP Reply failed";
+ }
+}
+
+static void bond_state_changed_cb(bt_status_t status,
+ bt_bdaddr_t* remote_bd_addr,
+ bt_bond_state_t state) {
+ LOG(INFO) << "Bond State Changed = " << state;
+ global_pair_state = state;
+}
+
+static void acl_state_changed(bt_status_t status, bt_bdaddr_t* remote_bd_addr,
+ bt_acl_state_t state) {
+ bdstr_t bd_addr_str;
+ bdaddr_to_string(remote_bd_addr, bd_addr_str, sizeof(bd_addr_str));
+ LOG(INFO) << __func__ << ": remote_bd_addr=" << bd_addr_str << ", acl status="
+ << (state == BT_ACL_STATE_CONNECTED ? "ACL Connected"
+ : "ACL Disconnected");
+}
+
+static void dut_mode_recv(uint16_t opcode, uint8_t* buf, uint8_t len) {
+ LOG(INFO) << "DUT MODE RECV : NOT IMPLEMENTED";
+}
+
+static bt_callbacks_t bt_callbacks = {
+ sizeof(bt_callbacks_t),
+ adapter_state_changed,
+ adapter_properties_changed, /*adapter_properties_cb */
+ nullptr, /* remote_device_properties_cb */
+ nullptr, /* device_found_cb */
+ discovery_state_changed, /* discovery_state_changed_cb */
+ pin_request_cb, /* pin_request_cb */
+ ssp_request_cb, /* ssp_request_cb */
+ bond_state_changed_cb, /*bond_state_changed_cb */
+ acl_state_changed, /* acl_state_changed_cb */
+ nullptr, /* thread_evt_cb */
+ dut_mode_recv, /*dut_mode_recv_cb */
+ nullptr, /* le_test_mode_cb */
+ nullptr /* energy_info_cb */
+};
+
+static bool set_wake_alarm(uint64_t delay_millis, bool should_wake, alarm_cb cb,
+ void* data) {
+ static timer_t timer;
+ static bool timer_created;
+
+ if (!timer_created) {
+ struct sigevent sigevent;
+ memset(&sigevent, 0, sizeof(sigevent));
+ sigevent.sigev_notify = SIGEV_THREAD;
+ sigevent.sigev_notify_function = (void (*)(union sigval))cb;
+ sigevent.sigev_value.sival_ptr = data;
+ timer_create(CLOCK_MONOTONIC, &sigevent, &timer);
+ timer_created = true;
+ }
+
+ struct itimerspec new_value;
+ new_value.it_value.tv_sec = delay_millis / 1000;
+ new_value.it_value.tv_nsec = (delay_millis % 1000) * 1000 * 1000;
+ new_value.it_interval.tv_sec = 0;
+ new_value.it_interval.tv_nsec = 0;
+ timer_settime(timer, 0, &new_value, nullptr);
+
+ return true;
+}
+
+static int acquire_wake_lock(const char* lock_name) {
+ return BT_STATUS_SUCCESS;
+}
+
+static int release_wake_lock(const char* lock_name) {
+ return BT_STATUS_SUCCESS;
+}
+
+static bt_os_callouts_t callouts = {
+ sizeof(bt_os_callouts_t), set_wake_alarm, acquire_wake_lock,
+ release_wake_lock,
+};
+
+void adapter_init(void) {
+ LOG(INFO) << __func__;
+ global_status = (bt_status_t)sBtInterface->init(&bt_callbacks);
+ if (global_status == BT_STATUS_SUCCESS) {
+ global_status = (bt_status_t)sBtInterface->set_os_callouts(&callouts);
+ }
+ check_return_status(global_status);
+}
+
+void adapter_enable(void) {
+ LOG(INFO) << __func__;
+ if (global_bt_enabled) {
+ LOG(INFO) << __func__ << ": Bluetooth is already enabled";
+ return;
+ }
+ global_status = (bt_status_t)sBtInterface->enable(global_strict_mode);
+ check_return_status(global_status);
+}
+
+void adapter_disable(void) {
+ LOG(INFO) << __func__;
+ if (!global_bt_enabled) {
+ LOG(INFO) << __func__ << ": Bluetooth is already disabled";
+ return;
+ }
+ global_status = (bt_status_t)sBtInterface->disable();
+ check_return_status(global_status);
+}
+void adapter_dut_mode_configure(char* p) {
+ LOG(INFO) << __func__;
+ if (!global_bt_enabled) {
+ LOG(INFO) << __func__
+ << ": Bluetooth must be enabled for test_mode to work.";
+ return;
+ }
+ int32_t mode = get_signed_int(&p, -1); // arg1
+ if ((mode != 0) && (mode != 1)) {
+ LOG(INFO) << __func__ << "Please specify mode: 1 to enter, 0 to exit";
+ return;
+ }
+ global_status = (bt_status_t)sBtInterface->dut_mode_configure(mode);
+ check_return_status(global_status);
+}
+
+void adapter_cleanup(void) {
+ LOG(INFO) << __func__;
+ sBtInterface->cleanup();
+}
+
+/*******************************************************************************
+ ** Console commands
+ *******************************************************************************/
+
+void do_help(char* p) {
+ int i = 0;
+ char line[128];
+ int pos = 0;
+
+ while (console_cmd_list[i].name != nullptr) {
+ pos = snprintf(line, sizeof(line), "%s", (char*)console_cmd_list[i].name);
+ printf("%s %s\n", (char*)line, (char*)console_cmd_list[i].help);
+ i++;
+ }
+}
+
+void do_quit(char* p) { console_shutdown(); }
+
+/*******************************************************************
+ *
+ * BT TEST CONSOLE COMMANDS
+ *
+ * Parses argument lists and passes to API test function
+ *
+ */
+
+void do_init(char* p) { adapter_init(); }
+
+void do_enable(char* p) { adapter_enable(); }
+
+void do_disable(char* p) { adapter_disable(); }
+
+void do_cleanup(char* p) { adapter_cleanup(); }
+
+/**
+ * MCAP API commands
+ */
+void do_mcap_register(char* p) {
+ uint16_t ctrl_psm = get_hex(&p, 0); // arg1
+ uint16_t data_psm = get_hex(&p, 0); // arg2
+ uint16_t sec_mask = get_int(&p, 0); // arg3
+ printf("%s: ctrl_psm=0x%04x, data_psm=0x%04x, sec_mask=0x%04x\n", __func__,
+ ctrl_psm, data_psm, sec_mask);
+ if (!ctrl_psm || !data_psm) {
+ printf("%s: Invalid Parameters\n", __func__);
+ return;
+ }
+ sMcapTestApp->Register(ctrl_psm, data_psm, sec_mask, mcap_ctrl_callback);
+ printf("%s: mcap_handle=%d\n", __func__, sMcapTestApp->GetHandle());
+}
+
+void do_mcap_deregister(char* p) {
+ printf("%s: mcap_handle=%d\n", __func__, sMcapTestApp->GetHandle());
+ sMcapTestApp->Deregister();
+ printf("%s: handle=%d\n", __func__, sMcapTestApp->GetHandle());
+}
+
+void do_mcap_create_mdep(char* p) {
+ int type = get_int(&p, -1); // arg1
+ printf("%s: mcap_handle=%d, type=%d\n", __func__, sMcapTestApp->GetHandle(),
+ type);
+ bool ret = sMcapTestApp->CreateMdep(type, MCA_NUM_MDLS, mcap_data_cb);
+ printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_delete_mdep(char* p) {
+ uint8_t mdep_handle = get_int(&p, 0);
+ printf("%s: mcap_handle=%d, mdep_handle=%d\n", __func__,
+ sMcapTestApp->GetHandle(), mdep_handle);
+ if (!mdep_handle) {
+ printf("%s: Invalid Parameters\n", __func__);
+ return;
+ }
+ McapMdep* mcap_mdep = sMcapTestApp->FindMdepByHandle(mdep_handle);
+ if (!mcap_mdep) {
+ LOG(ERROR) << "No MDEP for handle " << (int)mdep_handle;
+ return;
+ }
+ bool ret = mcap_mdep->Delete();
+ printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_connect_mcl(char* p) {
+ char buf[64];
+ get_str(&p, buf); // arg1
+ bt_bdaddr_t bd_addr;
+ bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+ uint16_t ctrl_psm = get_hex(&p, 0); // arg2
+ uint16_t sec_mask = get_int(&p, 0); // arg3
+ printf("%s: mcap_handle=%d, ctrl_psm=0x%04x, secMask=0x%04x, bd_addr=%s\n",
+ __func__, sMcapTestApp->GetHandle(), ctrl_psm, sec_mask, buf);
+ if (!ctrl_psm || !valid_bd_addr) {
+ printf("%s: Invalid Parameters\n", __func__);
+ return;
+ }
+ bool ret = sMcapTestApp->ConnectMcl(bd_addr, ctrl_psm, sec_mask);
+ printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_disconnect_mcl(char* p) {
+ char buf[64];
+ get_str(&p, buf); // arg1
+ bt_bdaddr_t bd_addr;
+ bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+ printf("%s: bd_addr=%s\n", __func__, buf);
+ if (!valid_bd_addr) {
+ printf("%s: Invalid Parameters\n", __func__);
+ return;
+ }
+ McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for bd_addr " << buf;
+ return;
+ }
+ bool ret = mcap_mcl->Disconnect();
+ printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_create_mdl(char* p) {
+ char buf[64];
+ get_str(&p, buf); // arg1
+ bt_bdaddr_t bd_addr;
+ bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+ uint16_t mdep_handle = get_int(&p, 0); // arg2
+ uint16_t data_psm = get_hex(&p, 0); // arg3
+ uint16_t mdl_id = get_int(&p, 0); // arg4
+ uint8_t peer_dep_id = get_int(&p, 0); // arg5
+ uint8_t cfg = get_hex(&p, 0); // arg6
+ int do_not_connect = get_int(&p, 0); // arg7
+ printf(
+ "%s: bd_addr=%s, mdep_handle=%d, data_psm=0x%04x, mdl_id=%d,"
+ " peer_dep_id=%d, cfg=0x%02x, do_not_connect=%d\n",
+ __func__, buf, mdep_handle, data_psm, mdl_id, peer_dep_id, cfg,
+ do_not_connect);
+ if (!data_psm || !peer_dep_id || !valid_bd_addr || !mdep_handle) {
+ printf("%s: Invalid Parameters\n", __func__);
+ return;
+ }
+ McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for bd_addr " << buf;
+ return;
+ }
+ bool ret = mcap_mcl->CreateMdl(mdep_handle, data_psm, mdl_id, peer_dep_id,
+ cfg, !do_not_connect);
+ printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_data_channel_config(char* p) {
+ char buf[64];
+ get_str(&p, buf); // arg1
+ bt_bdaddr_t bd_addr;
+ bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+ printf("%s: bd_addr=%s\n", __func__, buf);
+ if (!valid_bd_addr) {
+ printf("%s: Invalid Parameters\n", __func__);
+ return;
+ }
+ McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for bd_addr " << buf;
+ return;
+ }
+ bool ret = mcap_mcl->DataChannelConfig();
+ printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_abort_mdl(char* p) {
+ char buf[64];
+ get_str(&p, buf); // arg1
+ bt_bdaddr_t bd_addr;
+ bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+ printf("%s: bd_addr=%s\n", __func__, buf);
+ if (!valid_bd_addr) {
+ printf("%s: Invalid Parameters\n", __func__);
+ return;
+ }
+ McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for bd_addr " << buf;
+ return;
+ }
+ bool ret = mcap_mcl->AbortMdl();
+ printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_delete_mdl(char* p) {
+ char buf[64];
+ get_str(&p, buf); // arg1
+ bt_bdaddr_t bd_addr;
+ bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+ uint16_t mdl_id = get_int(&p, 0); // arg2
+ printf("%s: bd_addr=%s, mdl_id=%d\n", __func__, buf, mdl_id);
+ if (!valid_bd_addr) {
+ printf("%s: Invalid Parameters\n", __func__);
+ return;
+ }
+ McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for bd_addr " << buf;
+ return;
+ }
+ bool ret = mcap_mcl->DeleteMdl(mdl_id);
+ printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_close_mdl(char* p) {
+ char buf[64];
+ get_str(&p, buf); // arg1
+ bt_bdaddr_t bd_addr;
+ bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+ uint16_t mdl_id = get_int(&p, 0); // arg2
+ printf("%s: bd_addr=%s, mdl_id=%d\n", __func__, buf, mdl_id);
+ if (!valid_bd_addr || !mdl_id) {
+ printf("%s: Invalid Parameters\n", __func__);
+ return;
+ }
+ McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for bd_addr " << buf;
+ return;
+ }
+ McapMdl* mcap_mdl = mcap_mcl->FindMdlById(mdl_id);
+ if (!mcap_mdl) {
+ LOG(ERROR) << "No MDL for ID " << (int)mdl_id;
+ return;
+ }
+ bool ret = mcap_mdl->Close();
+ printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_reconnect_mdl(char* p) {
+ char buf[64];
+ get_str(&p, buf); // arg1
+ bt_bdaddr_t bd_addr;
+ bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+ uint16_t data_psm = get_hex(&p, 0); // arg1
+ uint16_t mdl_id = get_int(&p, 0); // arg2
+ printf("%s: data_psm=0x%04x, mdl_id=%d\n", __func__, data_psm, mdl_id);
+ if (!valid_bd_addr) {
+ printf("%s: Invalid Parameters\n", __func__);
+ return;
+ }
+ McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+ if (!mcap_mcl) {
+ LOG(ERROR) << "No MCL for bd_addr " << buf;
+ return;
+ }
+ McapMdl* mcap_mdl = mcap_mcl->FindMdlById(mdl_id);
+ if (!mcap_mdl) {
+ LOG(ERROR) << "No MDL for ID " << (int)mdl_id;
+ return;
+ }
+ bool ret = mcap_mdl->Reconnect(data_psm);
+ printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_pairing(char* p) {
+ bt_bdaddr_t bd_addr;
+ if (!string_to_bdaddr(p, &bd_addr)) {
+ LOG(ERROR) << "Invalid Bluetooth address " << p;
+ return;
+ }
+ if (BT_STATUS_SUCCESS !=
+ sBtInterface->create_bond(&bd_addr, BT_TRANSPORT_BR_EDR)) {
+ LOG(ERROR) << "Failed to Initiate Pairing";
+ return;
+ }
+}
+
+/** CONSOLE COMMAND TABLE */
+
+const cmd_t console_cmd_list[] = {
+ /* INTERNAL */
+ {"help", do_help, "", 0},
+ {"quit", do_quit, "", 0},
+ /* API CONSOLE COMMANDS */
+ /* Init and Cleanup shall be called automatically */
+ {"enable_bluetooth", do_enable, "", 0},
+ {"disable_bluetooth", do_disable, "", 0},
+ {"pair", do_pairing, "BD_ADDR<xx:xx:xx:xx:xx:xx>", 0},
+ {"register", do_mcap_register,
+ "ctrl_psm<hex> data_psm<hex> security_mask<0-10>", 0},
+ {"deregister", do_mcap_deregister, "", 0},
+ {"create_mdep", do_mcap_create_mdep, "type<0-Echo, 1-Normal>", 0},
+ {"delete_mdep", do_mcap_delete_mdep, "mdep_handle<int>", 0},
+ {"connect_mcl", do_mcap_connect_mcl,
+ "BD_ADDR<xx:xx:xx:xx:xx:xx> ctrl_psm<hex> security_mask<0-10>", 0},
+ {"disconnect_mcl", do_mcap_disconnect_mcl, "BD_ADDR<xx:xx:xx:xx:xx:xx>", 0},
+ {"create_mdl", do_mcap_create_mdl,
+ "BD_ADDR<xx:xx:xx:xx:xx:xx> mdep_handle<int> data_psm<hex> mdl_id<int> "
+ "peer_dep_id<int> cfg<hex> "
+ "do_not_connect<0-connect,1-wait_for_data_channel_config>",
+ 0},
+ {"data_channel_config", do_mcap_data_channel_config,
+ "BD_ADDR<xx:xx:xx:xx:xx:xx>", 0},
+ {"abort_mdl", do_mcap_abort_mdl, "BD_ADDR<xx:xx:xx:xx:xx:xx>", 0},
+ {"close_mdl", do_mcap_close_mdl, "BD_ADDR<xx:xx:xx:xx:xx:xx> mdl_id<int>",
+ 0},
+ {"delete_mdl", do_mcap_delete_mdl, "BD_ADDR<xx:xx:xx:xx:xx:xx> mdl_id<int>",
+ 0},
+ {"reconnect_mdl", do_mcap_reconnect_mdl,
+ "BD_ADDR<xx:xx:xx:xx:xx:xx> data_psm<hex> mdl_id<int>", 0},
+ /* last entry */
+ {nullptr, nullptr, "", 0},
+};
+
+/** Main console command handler */
+
+static void process_cmd(char* p, bool is_job) {
+ char cmd[2048];
+ int i = 0;
+ char* p_saved = p;
+
+ get_str(&p, cmd); // arg1
+
+ /* table commands */
+ while (console_cmd_list[i].name != nullptr) {
+ if (is_cmd(cmd, console_cmd_list[i].name)) {
+ if (!is_job && console_cmd_list[i].is_job)
+ create_cmdjob(p_saved);
+ else {
+ console_cmd_list[i].handler(p);
+ }
+ return;
+ }
+ i++;
+ }
+ LOG(ERROR) << "Unknown command: " << p_saved;
+ do_help(nullptr);
+}
+
+int main(int argc, char* argv[]) {
+ setbuf(stdout, NULL);
+#if !defined(OS_GENERIC)
+ config_permissions();
+#endif
+ LOG(INFO) << "Fluoride MCAP test app is starting";
+
+ if (HAL_load() < 0) {
+ fprintf(stderr, "%s: HAL failed to initialize, exit\n", __func__);
+ unlink(PID_FILE);
+ exit(0);
+ }
+
+ setup_test_env();
+
+ /* Automatically perform the init */
+ adapter_init();
+ sleep(2);
+ adapter_enable();
+ sleep(2);
+ sMcapTestInterface =
+ (btmcap_test_interface_t*)sBtInterface->get_profile_interface(
+ BT_TEST_INTERFACE_MCAP_ID);
+ sMcapTestInterface->init();
+ sMcapTestApp = new McapTestApp(sMcapTestInterface);
+
+ /* Main loop */
+ char line[2048];
+ while (!global_main_done) {
+ memset(line, '\0', sizeof(line));
+ /* command prompt */
+ printf(">");
+ fflush(stdout);
+ fgets(line, sizeof(line), stdin);
+ if (line[0] != '\0') {
+ /* Remove line feed */
+ line[strlen(line) - 1] = 0;
+ if (strlen(line) != 0) process_cmd(line, false);
+ }
+ }
+ adapter_cleanup();
+ HAL_unload();
+ LOG(INFO) << "Fluoride MCAP test app is terminating";
+
+ return 0;
+}