1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include <cstring>
17
18#include <base/logging.h>
19
20#include "mca_api.h"
21#include "mca_defs.h"
22#include "mcap_test_mcl.h"
23
24namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
25
26McapMcl::McapMcl(btmcap_test_interface_t* mcap_test_interface,
27                 tMCA_HANDLE mcap_handle, const RawAddress& peer_bd_addr)
28    : _mdl_list() {
29  _mcap_handle = mcap_handle;
30  _mcap_test_interface = mcap_test_interface;
31  memcpy(_peer_bd_addr.address, peer_bd_addr.address,
32         sizeof(_peer_bd_addr.address));
33}
34
35bool McapMcl::Connect(uint16_t ctrl_psm, uint16_t sec_mask) {
36  tMCA_RESULT ret = _mcap_test_interface->connect_mcl(
37      _mcap_handle, _peer_bd_addr, ctrl_psm, sec_mask);
38  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
39  return ret == MCA_SUCCESS;
40}
41
42bool McapMcl::Disconnect() {
43  if (!IsConnected()) {
44    LOG(ERROR) << "MCL is not connected";
45    return false;
46  }
47  tMCA_RESULT ret = _mcap_test_interface->disconnect_mcl(_mcl_handle);
48  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
49  return ret == MCA_SUCCESS;
50}
51
52McapMdl* McapMcl::AllocateMdl(tMCA_DEP mdep_handle, uint16_t mdl_id,
53                              uint8_t dep_id, uint8_t cfg) {
54  if (!IsConnected()) {
55    LOG(ERROR) << "MCL is not connected";
56    return nullptr;
57  }
58  if (FindMdlById(mdl_id) != nullptr) {
59    LOG(ERROR) << "mdl_id=" << mdl_id << "already exists";
60    return nullptr;
61  }
62  if (!HasAvailableMdl()) {
63    LOG(ERROR) << "No more avaible MDL, currently " << _mdl_list.size();
64    return nullptr;
65  }
66  _mdl_list.push_back(McapMdl(_mcap_test_interface, _mcl_handle, mdep_handle,
67                              mdl_id, dep_id, cfg));
68  return &_mdl_list[_mdl_list.size() - 1];
69}
70
71bool McapMcl::CreateMdl(tMCA_DEP mdep_handle, uint16_t data_psm,
72                        uint16_t mdl_id, uint8_t peer_dep_id, uint8_t cfg,
73                        bool should_connect) {
74  if (!IsConnected()) {
75    LOG(ERROR) << "MCL is not connected";
76    return false;
77  }
78  McapMdl* mcap_mdl = FindMdlById(mdl_id);
79  if (!mcap_mdl) {
80    LOG(INFO) << "mdl_id=" << mdl_id << "does not exists, creating new one";
81    mcap_mdl = AllocateMdl(mdep_handle, mdl_id, peer_dep_id, cfg);
82    if (!mcap_mdl) {
83      return false;
84    }
85  }
86  if (mcap_mdl->IsConnected()) {
87    LOG(ERROR) << "mdl_id=" << mdl_id << "is already connected with handle "
88               << (int)mcap_mdl->GetHandle();
89    return false;
90  }
91  return mcap_mdl->Create(data_psm, should_connect);
92}
93
94bool McapMcl::DataChannelConfig() {
95  tMCA_RESULT ret = _mcap_test_interface->data_channel_config(
96      _mcl_handle, get_test_channel_config());
97  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
98  return ret == MCA_SUCCESS;
99}
100
101bool McapMcl::CreateMdlResponse(tMCA_DEP mdep_handle, uint16_t mdl_id,
102                                uint8_t my_dep_id, uint8_t cfg) {
103  if (!IsConnected()) {
104    LOG(ERROR) << "MCL is not connected";
105    return false;
106  }
107  McapMdl* mcap_mdl = FindMdlById(mdl_id);
108  if (!mcap_mdl) {
109    LOG(INFO) << "mdl_id=" << mdl_id << " does not exists, creating new one";
110    mcap_mdl = AllocateMdl(mdep_handle, mdl_id, my_dep_id, cfg);
111    if (!mcap_mdl) {
112      LOG(ERROR) << "MDL cannot be created";
113      return false;
114    }
115  }
116  if (mcap_mdl->IsConnected()) {
117    LOG(INFO) << "mdl_id=" << mdl_id << " is already connected with handle "
118              << (int)mcap_mdl->GetHandle() << ", updating context";
119    mcap_mdl->UpdateContext(mdep_handle, my_dep_id, cfg);
120  }
121  return mcap_mdl->CreateResponse();
122}
123
124bool McapMcl::AbortMdl() {
125  tMCA_RESULT ret = _mcap_test_interface->abort_mdl(_mcl_handle);
126  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
127  return ret == MCA_SUCCESS;
128}
129
130bool McapMcl::DeleteMdl(uint16_t mdl_id) {
131  tMCA_RESULT ret = _mcap_test_interface->delete_mdl(_mcl_handle, mdl_id);
132  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
133  return ret == MCA_SUCCESS;
134}
135
136RawAddress& McapMcl::GetPeerAddress() { return _peer_bd_addr; }
137
138void McapMcl::SetHandle(tMCA_CL handle) { _mcl_handle = handle; }
139
140tMCA_CL McapMcl::GetHandle() const { return _mcl_handle; }
141
142void McapMcl::SetMtu(uint16_t mtu) { _control_mtu = mtu; }
143
144uint16_t McapMcl::GetMtu() { return _control_mtu; }
145
146McapMdl* McapMcl::FindMdlById(uint16_t mdl_id) {
147  for (McapMdl& mdl : _mdl_list) {
148    if (mdl.GetId() == mdl_id) {
149      return &mdl;
150    }
151  }
152  return nullptr;
153}
154
155McapMdl* McapMcl::FindMdlByHandle(tMCA_DL mdl_handle) {
156  for (McapMdl& mdl : _mdl_list) {
157    if (mdl.GetHandle() == mdl_handle) {
158      return &mdl;
159    }
160  }
161  return nullptr;
162}
163
164void McapMcl::RemoveAllMdl() { _mdl_list.clear(); }
165
166void McapMcl::RemoveMdl(uint16_t mdl_id) {
167  LOG(INFO) << "Removing MDL id " << (int)mdl_id;
168  for (std::vector<McapMdl>::iterator it = _mdl_list.begin();
169       it != _mdl_list.end(); ++it) {
170    if (it->GetId() == mdl_id) {
171      _mdl_list.erase(it);
172      LOG(INFO) << "Removed MDL id " << (int)mdl_id;
173      return;
174    }
175  }
176}
177
178void McapMcl::ResetAllMdl() {
179  for (McapMdl& mcap_mdl : _mdl_list) {
180    mcap_mdl.SetHandle(0);
181    mcap_mdl.SetMtu(0);
182    mcap_mdl.SetResponseCode(-1);
183  }
184}
185
186void McapMcl::ResetMdl(uint16_t mdl_id) {
187  LOG(INFO) << "Closing MDL id " << (int)mdl_id;
188  McapMdl* mcap_mdl = FindMdlById(mdl_id);
189  if (!mcap_mdl) {
190    LOG(ERROR) << "Cannot find MDL for id " << (int)mdl_id;
191    return;
192  }
193  if (mcap_mdl->IsConnected()) {
194    LOG(ERROR) << "MDL " << (int)mdl_id << " is still connected";
195    return;
196  }
197  mcap_mdl->SetHandle(0);
198  mcap_mdl->SetMtu(0);
199  mcap_mdl->SetResponseCode(-1);
200}
201
202bool McapMcl::IsConnected() { return _mcl_handle > 0; }
203
204int McapMcl::ConnectedMdlCount() {
205  int count = 0;
206  for (McapMdl& mcap_mdl : _mdl_list) {
207    if (mcap_mdl.IsConnected()) {
208      count++;
209    }
210  }
211  return count;
212}
213
214bool McapMcl::HasAvailableMdl() { return ConnectedMdlCount() < MCA_NUM_MDLS; }
215
216}  // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
217