1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/l2cap/a2dp_offload_manager.h"
16
17 #include <pw_bluetooth/hci_android.emb.h>
18
19 #include <cstdint>
20 #include <utility>
21
22 #include "pw_bluetooth_sapphire/internal/host/common/host_error.h"
23 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
24 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
25 #include "pw_bluetooth_sapphire/internal/host/hci-spec/vendor_protocol.h"
26 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel.h"
27 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
28 #include "pw_bluetooth_sapphire/internal/host/transport/control_packets.h"
29 #include "pw_bluetooth_sapphire/internal/host/transport/emboss_control_packets.h"
30
31 namespace bt::l2cap {
32 namespace hci_android = bt::hci_spec::vendor::android;
33
StartA2dpOffload(const Configuration & config,ChannelId local_id,ChannelId remote_id,hci_spec::ConnectionHandle link_handle,uint16_t max_tx_sdu_size,hci::ResultCallback<> callback)34 void A2dpOffloadManager::StartA2dpOffload(
35 const Configuration& config,
36 ChannelId local_id,
37 ChannelId remote_id,
38 hci_spec::ConnectionHandle link_handle,
39 uint16_t max_tx_sdu_size,
40 hci::ResultCallback<> callback) {
41 BT_DEBUG_ASSERT(cmd_channel_.is_alive());
42
43 switch (a2dp_offload_status_) {
44 case A2dpOffloadStatus::kStarted: {
45 bt_log(WARN,
46 "l2cap",
47 "Only one channel can offload A2DP at a time; already offloaded "
48 "(handle: %#.4x, local id: %#.4x",
49 *offloaded_link_handle_,
50 *offloaded_channel_id_);
51 callback(ToResult(HostError::kInProgress));
52 return;
53 }
54 case A2dpOffloadStatus::kStarting: {
55 bt_log(WARN,
56 "l2cap",
57 "A2DP offload is currently starting (status: %hhu)",
58 static_cast<unsigned char>(a2dp_offload_status_));
59 callback(ToResult(HostError::kInProgress));
60 return;
61 }
62 case A2dpOffloadStatus::kStopping: {
63 bt_log(WARN,
64 "l2cap",
65 "A2DP offload is stopping... wait until stopped before starting "
66 "(status: %hhu)",
67 static_cast<unsigned char>(a2dp_offload_status_));
68 callback(ToResult(HostError::kInProgress));
69 return;
70 }
71 case A2dpOffloadStatus::kStopped:
72 break;
73 }
74
75 offloaded_link_handle_ = link_handle;
76 offloaded_channel_id_ = local_id;
77 a2dp_offload_status_ = A2dpOffloadStatus::kStarting;
78
79 constexpr size_t kPacketSize = pw::bluetooth::vendor::android_hci::
80 StartA2dpOffloadCommand::MaxSizeInBytes();
81 auto packet = hci::EmbossCommandPacket::New<
82 pw::bluetooth::vendor::android_hci::StartA2dpOffloadCommandWriter>(
83 hci_android::kA2dpOffloadCommand, kPacketSize);
84 auto packet_view = packet.view_t();
85
86 packet_view.vendor_command().sub_opcode().Write(
87 hci_android::kStartA2dpOffloadCommandSubopcode);
88 packet_view.codec_type().Write(
89 static_cast<pw::bluetooth::vendor::android_hci::A2dpCodecType>(
90 config.codec));
91 packet_view.max_latency().Write(config.max_latency);
92
93 packet_view.scms_t_enable().enabled().Write(config.scms_t_enable.enabled);
94 packet_view.scms_t_enable().header().Write(config.scms_t_enable.header);
95
96 packet_view.sampling_frequency().Write(
97 static_cast<pw::bluetooth::vendor::android_hci::A2dpSamplingFrequency>(
98 config.sampling_frequency));
99 packet_view.bits_per_sample().Write(
100 static_cast<pw::bluetooth::vendor::android_hci::A2dpBitsPerSample>(
101 config.bits_per_sample));
102 packet_view.channel_mode().Write(
103 static_cast<pw::bluetooth::vendor::android_hci::A2dpChannelMode>(
104 config.channel_mode));
105 packet_view.encoded_audio_bitrate().Write(config.encoded_audio_bit_rate);
106 packet_view.connection_handle().Write(link_handle);
107 packet_view.l2cap_channel_id().Write(remote_id);
108 packet_view.l2cap_mtu_size().Write(max_tx_sdu_size);
109
110 // kAptx and kAptxhd codecs not yet handled
111 if (config.codec == hci_android::A2dpCodecType::kSbc) {
112 auto sbc_codec_information =
113 packet_view.sbc_codec_information().BackingStorage();
114 MutableBufferView sbc_codec_information_buf = MutableBufferView(
115 sbc_codec_information.data(), sbc_codec_information.SizeInBytes());
116 sbc_codec_information_buf.Fill(0);
117 sbc_codec_information_buf.WriteObj(config.codec_information.sbc);
118 } else if (config.codec == hci_android::A2dpCodecType::kLdac) {
119 auto ldac_codec_information =
120 packet_view.ldac_codec_information().BackingStorage();
121 MutableBufferView ldac_codec_information_buf = MutableBufferView(
122 ldac_codec_information.data(), ldac_codec_information.SizeInBytes());
123 ldac_codec_information_buf.Fill(0);
124 ldac_codec_information_buf.WriteObj(config.codec_information.ldac);
125 } else if (config.codec == hci_android::A2dpCodecType::kAac) {
126 auto aac_codec_information =
127 packet_view.aac_codec_information().BackingStorage();
128 MutableBufferView aac_codec_information_buf = MutableBufferView(
129 aac_codec_information.data(), aac_codec_information.SizeInBytes());
130 aac_codec_information_buf.Fill(0);
131 aac_codec_information_buf.WriteObj(config.codec_information.aac);
132 }
133
134 cmd_channel_->SendCommand(
135 std::move(packet),
136 [cb = std::move(callback),
137 id = local_id,
138 handle = link_handle,
139 self = weak_self_.GetWeakPtr(),
140 this](auto /*transaction_id*/, const hci::EventPacket& event) mutable {
141 if (!self.is_alive()) {
142 return;
143 }
144
145 if (event.ToResult().is_error()) {
146 bt_log(WARN,
147 "l2cap",
148 "Start A2DP offload command failed (result: %s, handle: "
149 "%#.4x, local id: %#.4x)",
150 bt_str(event.ToResult()),
151 handle,
152 id);
153 a2dp_offload_status_ = A2dpOffloadStatus::kStopped;
154 } else {
155 bt_log(INFO,
156 "l2cap",
157 "A2DP offload started (handle: %#.4x, local id: %#.4x",
158 handle,
159 id);
160 a2dp_offload_status_ = A2dpOffloadStatus::kStarted;
161 }
162 cb(event.ToResult());
163
164 // If we tried to stop while A2DP was still starting, perform the stop
165 // command now
166 if (pending_stop_a2dp_offload_request_.has_value()) {
167 auto callback = std::move(pending_stop_a2dp_offload_request_.value());
168 pending_stop_a2dp_offload_request_.reset();
169
170 RequestStopA2dpOffload(id, handle, std::move(callback));
171 }
172 });
173 }
174
RequestStopA2dpOffload(ChannelId local_id,hci_spec::ConnectionHandle link_handle,hci::ResultCallback<> callback)175 void A2dpOffloadManager::RequestStopA2dpOffload(
176 ChannelId local_id,
177 hci_spec::ConnectionHandle link_handle,
178 hci::ResultCallback<> callback) {
179 BT_DEBUG_ASSERT(cmd_channel_.is_alive());
180
181 switch (a2dp_offload_status_) {
182 case A2dpOffloadStatus::kStopped: {
183 bt_log(DEBUG,
184 "l2cap",
185 "No channels are offloading A2DP (status: %hhu)",
186 static_cast<unsigned char>(a2dp_offload_status_));
187 callback(fit::success());
188 return;
189 }
190 case A2dpOffloadStatus::kStopping: {
191 bt_log(WARN,
192 "l2cap",
193 "A2DP offload is currently stopping (status: %hhu)",
194 static_cast<unsigned char>(a2dp_offload_status_));
195 callback(ToResult(HostError::kInProgress));
196 return;
197 }
198 case A2dpOffloadStatus::kStarting:
199 case A2dpOffloadStatus::kStarted:
200 break;
201 }
202
203 if (!IsChannelOffloaded(local_id, link_handle)) {
204 callback(fit::success());
205 return;
206 }
207
208 // Wait until offloading status is |kStarted| before sending stop command
209 if (a2dp_offload_status_ == A2dpOffloadStatus::kStarting) {
210 pending_stop_a2dp_offload_request_ = std::move(callback);
211 return;
212 }
213
214 a2dp_offload_status_ = A2dpOffloadStatus::kStopping;
215
216 auto packet = hci::EmbossCommandPacket::New<
217 pw::bluetooth::vendor::android_hci::StopA2dpOffloadCommandWriter>(
218 hci_android::kA2dpOffloadCommand);
219 auto packet_view = packet.view_t();
220
221 packet_view.vendor_command().sub_opcode().Write(
222 hci_android::kStopA2dpOffloadCommandSubopcode);
223
224 cmd_channel_->SendCommand(
225 std::move(packet),
226 [cb = std::move(callback),
227 self = weak_self_.GetWeakPtr(),
228 id = local_id,
229 handle = link_handle,
230 this](auto /*transaction_id*/, const hci::EventPacket& event) mutable {
231 if (!self.is_alive()) {
232 return;
233 }
234
235 if (event.ToResult().is_error()) {
236 bt_log(WARN,
237 "l2cap",
238 "Stop A2DP offload command failed (result: %s, handle: %#.4x, "
239 "local id: %#.4x)",
240 bt_str(event.ToResult()),
241 handle,
242 id);
243 } else {
244 bt_log(INFO,
245 "l2cap",
246 "A2DP offload stopped (handle: %#.4x, local id: %#.4x",
247 handle,
248 id);
249 }
250 cb(event.ToResult());
251
252 a2dp_offload_status_ = A2dpOffloadStatus::kStopped;
253 });
254 }
255
IsChannelOffloaded(ChannelId id,hci_spec::ConnectionHandle link_handle) const256 bool A2dpOffloadManager::IsChannelOffloaded(
257 ChannelId id, hci_spec::ConnectionHandle link_handle) const {
258 if (!offloaded_channel_id_.has_value() ||
259 !offloaded_link_handle_.has_value()) {
260 bt_log(DEBUG,
261 "l2cap",
262 "Channel is not offloaded (handle: %#.4x, local id: %#.4x) ",
263 link_handle,
264 id);
265 return false;
266 }
267
268 // Same channel that requested start A2DP offloading must request stop
269 // offloading
270 if (id != offloaded_channel_id_ || link_handle != offloaded_link_handle_) {
271 bt_log(WARN,
272 "l2cap",
273 "Offloaded channel must request stop offloading; offloaded channel "
274 "(handle: %#.4x, local id: %#.4x)",
275 *offloaded_link_handle_,
276 *offloaded_channel_id_);
277 return false;
278 }
279
280 return id == *offloaded_channel_id_ &&
281 link_handle == *offloaded_link_handle_ &&
282 (a2dp_offload_status_ == A2dpOffloadStatus::kStarted ||
283 a2dp_offload_status_ == A2dpOffloadStatus::kStarting);
284 }
285
286 } // namespace bt::l2cap
287