1 /*
2 * Copyright 2021 HIMSA II K/S - www.himsa.com. Represented by EHIMA -
3 * www.ehima.com
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include "state_machine.h"
19
20 #include <base/bind.h>
21 #include <base/callback.h>
22
23 #include <map>
24
25 #include "bt_types.h"
26 #include "bta_gatt_queue.h"
27 #include "bta_le_audio_api.h"
28 #include "btm_iso_api.h"
29 #include "client_parser.h"
30 #include "codec_manager.h"
31 #include "content_control_id_keeper.h"
32 #include "devices.h"
33 #include "gd/common/strings.h"
34 #include "hcimsgs.h"
35 #include "le_audio_types.h"
36 #include "osi/include/alarm.h"
37 #include "osi/include/log.h"
38 #include "osi/include/osi.h"
39 #include "osi/include/properties.h"
40
41 // clang-format off
42 /* ASCS state machine 1.0
43 *
44 * State machine manages group of ASEs to make transition from one state to
45 * another according to specification and keeping involved necessary externals
46 * like: ISO, CIG, ISO data path, audio path form/to upper layer.
47 *
48 * GroupStream (API): GroupStream method of this le audio implementation class
49 * object should allow transition from Idle (No Caching),
50 * Codec Configured (Caching after release) state to
51 * Streaming for all ASEs in group within time limit. Time
52 * limit should keep safe whole state machine from being
53 * stucked in any in-middle state, which is not a destination
54 * state.
55 *
56 * TODO Second functionality of streaming should be switch
57 * context which will base on previous state, context type.
58 *
59 * GroupStop (API): GroupStop method of this le audio implementation class
60 * object should allow safe transition from any state to Idle
61 * or Codec Configured (if caching supported).
62 *
63 * ╔══════════════════╦═════════════════════════════╦══════════════╦══════════════════╦══════╗
64 * ║ Current State ║ ASE Control Point Operation ║ Result ║ Next State ║ Note ║
65 * ╠══════════════════╬═════════════════════════════╬══════════════╬══════════════════╬══════╣
66 * ║ Idle ║ Config Codec ║ Success ║ Codec Configured ║ + ║
67 * ║ Codec Configured ║ Config Codec ║ Success ║ Codec Configured ║ - ║
68 * ║ Codec Configured ║ Release ║ Success ║ Releasing ║ + ║
69 * ║ Codec Configured ║ Config QoS ║ Success ║ QoS Configured ║ + ║
70 * ║ QoS Configured ║ Config Codec ║ Success ║ Codec Configured ║ - ║
71 * ║ QoS Configured ║ Config QoS ║ Success ║ QoS Configured ║ - ║
72 * ║ QoS Configured ║ Release ║ Success ║ Releasing ║ + ║
73 * ║ QoS Configured ║ Enable ║ Success ║ Enabling ║ + ║
74 * ║ Enabling ║ Release ║ Success ║ Releasing ║ + ║
75 * ║ Enabling ║ Update Metadata ║ Success ║ Enabling ║ - ║
76 * ║ Enabling ║ Disable ║ Success ║ Disabling ║ - ║
77 * ║ Enabling ║ Receiver Start Ready ║ Success ║ Streaming ║ + ║
78 * ║ Streaming ║ Update Metadata ║ Success ║ Streaming ║ - ║
79 * ║ Streaming ║ Disable ║ Success ║ Disabling ║ + ║
80 * ║ Streaming ║ Release ║ Success ║ Releasing ║ + ║
81 * ║ Disabling ║ Receiver Stop Ready ║ Success ║ QoS Configured ║ + ║
82 * ║ Disabling ║ Release ║ Success ║ Releasing ║ + ║
83 * ║ Releasing ║ Released (no caching) ║ Success ║ Idle ║ + ║
84 * ║ Releasing ║ Released (caching) ║ Success ║ Codec Configured ║ - ║
85 * ╚══════════════════╩═════════════════════════════╩══════════════╩══════════════════╩══════╝
86 *
87 * + - supported transition
88 * - - not supported
89 */
90 // clang-format on
91
92 using bluetooth::common::ToString;
93 using bluetooth::hci::IsoManager;
94 using bluetooth::le_audio::GroupStreamStatus;
95 using le_audio::CodecManager;
96 using le_audio::LeAudioDevice;
97 using le_audio::LeAudioDeviceGroup;
98 using le_audio::LeAudioGroupStateMachine;
99
100 using le_audio::types::ase;
101 using le_audio::types::AseState;
102 using le_audio::types::AudioContexts;
103 using le_audio::types::AudioStreamDataPathState;
104 using le_audio::types::CigState;
105 using le_audio::types::CodecLocation;
106 using le_audio::types::LeAudioContextType;
107
108 namespace {
109
110 constexpr int linkQualityCheckInterval = 4000;
111
link_quality_cb(void * data)112 static void link_quality_cb(void* data) {
113 // very ugly, but we need to pass just two bytes
114 uint16_t cis_conn_handle = *((uint16_t*)data);
115
116 IsoManager::GetInstance()->ReadIsoLinkQuality(cis_conn_handle);
117 }
118
119 class LeAudioGroupStateMachineImpl;
120 LeAudioGroupStateMachineImpl* instance;
121
122 class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
123 public:
LeAudioGroupStateMachineImpl(Callbacks * state_machine_callbacks_)124 LeAudioGroupStateMachineImpl(Callbacks* state_machine_callbacks_)
125 : state_machine_callbacks_(state_machine_callbacks_),
126 watchdog_(alarm_new("LeAudioStateMachineTimer")) {}
127
~LeAudioGroupStateMachineImpl()128 ~LeAudioGroupStateMachineImpl() {
129 alarm_free(watchdog_);
130 watchdog_ = nullptr;
131 }
132
AttachToStream(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)133 bool AttachToStream(LeAudioDeviceGroup* group,
134 LeAudioDevice* leAudioDevice) override {
135 LOG(INFO) << __func__ << " group id: " << group->group_id_
136 << " device: " << leAudioDevice->address_;
137
138 /* This function is used to attach the device to the stream.
139 * Limitation here is that device should be previously in the streaming
140 * group and just got reconnected.
141 */
142 if (group->GetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
143 LOG_ERROR(" group not in the streaming state: %s",
144 ToString(group->GetState()).c_str());
145 return false;
146 }
147
148 auto context_type = group->GetConfigurationContextType();
149 auto metadata_context_type = group->GetMetadataContexts();
150
151 auto ccid = le_audio::ContentControlIdKeeper::GetInstance()->GetCcid(
152 static_cast<uint16_t>(context_type));
153 std::vector<uint8_t> ccids;
154 if (ccid != -1) {
155 ccids.push_back(static_cast<uint8_t>(ccid));
156 }
157
158 if (!group->Configure(context_type, metadata_context_type, ccids)) {
159 LOG_ERROR(" failed to set ASE configuration");
160 return false;
161 }
162
163 PrepareAndSendCodecConfigure(group, leAudioDevice);
164 return true;
165 }
166
StartStream(LeAudioDeviceGroup * group,le_audio::types::LeAudioContextType context_type,AudioContexts metadata_context_type,std::vector<uint8_t> ccid_list)167 bool StartStream(LeAudioDeviceGroup* group,
168 le_audio::types::LeAudioContextType context_type,
169 AudioContexts metadata_context_type,
170 std::vector<uint8_t> ccid_list) override {
171 LOG_INFO(" current state: %s", ToString(group->GetState()).c_str());
172
173 switch (group->GetState()) {
174 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
175 if (group->GetConfigurationContextType() == context_type) {
176 if (group->Activate(context_type)) {
177 SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
178 if (CigCreate(group)) {
179 return true;
180 }
181 }
182 LOG_INFO("Could not activate device, try to configure it again");
183 }
184
185 /* We are going to reconfigure whole group. Clear Cises.*/
186 ReleaseCisIds(group);
187
188 /* If configuration is needed */
189 FALLTHROUGH;
190 case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
191 if (!group->Configure(context_type, metadata_context_type, ccid_list)) {
192 LOG(ERROR) << __func__ << ", failed to set ASE configuration";
193 return false;
194 }
195
196 group->CigGenerateCisIds(context_type);
197 /* All ASEs should aim to achieve target state */
198 SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
199 PrepareAndSendCodecConfigure(group, group->GetFirstActiveDevice());
200 break;
201
202 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED: {
203 LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
204 if (!leAudioDevice) {
205 LOG(ERROR) << __func__ << ", group has no active devices";
206 return false;
207 }
208
209 /* All ASEs should aim to achieve target state */
210 SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
211 PrepareAndSendEnable(leAudioDevice);
212 break;
213 }
214
215 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING: {
216 /* This case just updates the metadata for the stream, in case
217 * stream configuration is satisfied. We can do that already for
218 * all the devices in a group, without any state transitions.
219 */
220 if (!group->IsMetadataChanged(metadata_context_type, ccid_list))
221 return true;
222
223 LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
224 if (!leAudioDevice) {
225 LOG(ERROR) << __func__ << ", group has no active devices";
226 return false;
227 }
228
229 while (leAudioDevice) {
230 PrepareAndSendUpdateMetadata(leAudioDevice, metadata_context_type,
231 ccid_list);
232 leAudioDevice = group->GetNextActiveDevice(leAudioDevice);
233 }
234 break;
235 }
236
237 default:
238 LOG_ERROR("Unable to transit from %s",
239 ToString(group->GetState()).c_str());
240 return false;
241 }
242
243 return true;
244 }
245
ConfigureStream(LeAudioDeviceGroup * group,le_audio::types::LeAudioContextType context_type,AudioContexts metadata_context_type,std::vector<uint8_t> ccid_list)246 bool ConfigureStream(LeAudioDeviceGroup* group,
247 le_audio::types::LeAudioContextType context_type,
248 AudioContexts metadata_context_type,
249 std::vector<uint8_t> ccid_list) override {
250 if (group->GetState() > AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED) {
251 LOG_ERROR(
252 "Stream should be stopped or in configured stream. Current state: %s",
253 ToString(group->GetState()).c_str());
254 return false;
255 }
256
257 ReleaseCisIds(group);
258
259 if (!group->Configure(context_type, metadata_context_type, ccid_list)) {
260 LOG_ERROR("Could not configure ASEs for group %d content type %d",
261 group->group_id_, int(context_type));
262
263 return false;
264 }
265
266 group->CigGenerateCisIds(context_type);
267 SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
268 PrepareAndSendCodecConfigure(group, group->GetFirstActiveDevice());
269
270 return true;
271 }
272
SuspendStream(LeAudioDeviceGroup * group)273 void SuspendStream(LeAudioDeviceGroup* group) override {
274 LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
275 LOG_ASSERT(leAudioDevice)
276 << __func__ << " Shouldn't be called without an active device.";
277
278 /* All ASEs should aim to achieve target state */
279 SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
280 PrepareAndSendDisable(leAudioDevice);
281 state_machine_callbacks_->StatusReportCb(group->group_id_,
282 GroupStreamStatus::SUSPENDING);
283 }
284
StopStream(LeAudioDeviceGroup * group)285 void StopStream(LeAudioDeviceGroup* group) override {
286 if (group->IsReleasingOrIdle()) {
287 LOG(INFO) << __func__ << ", group: " << group->group_id_
288 << " already in releasing process";
289 return;
290 }
291
292 LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
293 if (leAudioDevice == nullptr) {
294 LOG(ERROR) << __func__
295 << " Shouldn't be called without an active device.";
296 state_machine_callbacks_->StatusReportCb(group->group_id_,
297 GroupStreamStatus::IDLE);
298 return;
299 }
300
301 /* All Ases should aim to achieve target state */
302 SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
303 PrepareAndSendRelease(leAudioDevice);
304 state_machine_callbacks_->StatusReportCb(group->group_id_,
305 GroupStreamStatus::RELEASING);
306 }
307
ProcessGattNotifEvent(uint8_t * value,uint16_t len,struct ase * ase,LeAudioDevice * leAudioDevice,LeAudioDeviceGroup * group)308 void ProcessGattNotifEvent(uint8_t* value, uint16_t len, struct ase* ase,
309 LeAudioDevice* leAudioDevice,
310 LeAudioDeviceGroup* group) override {
311 struct le_audio::client_parser::ascs::ase_rsp_hdr arh;
312
313 ParseAseStatusHeader(arh, len, value);
314
315 LOG_INFO(" %s , ASE id: %d, state changed %s -> %s ",
316 leAudioDevice->address_.ToString().c_str(), +ase->id,
317 ToString(ase->state).c_str(),
318 ToString(AseState(arh.state)).c_str());
319
320 switch (static_cast<AseState>(arh.state)) {
321 case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
322 AseStateMachineProcessIdle(arh, ase, group, leAudioDevice);
323 break;
324 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
325 AseStateMachineProcessCodecConfigured(
326 arh, ase, value + le_audio::client_parser::ascs::kAseRspHdrMinLen,
327 len - le_audio::client_parser::ascs::kAseRspHdrMinLen, group,
328 leAudioDevice);
329 break;
330 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
331 AseStateMachineProcessQosConfigured(arh, ase, group, leAudioDevice);
332 break;
333 case AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING:
334 AseStateMachineProcessEnabling(arh, ase, group, leAudioDevice);
335 break;
336 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
337 AseStateMachineProcessStreaming(
338 arh, ase, value + le_audio::client_parser::ascs::kAseRspHdrMinLen,
339 len - le_audio::client_parser::ascs::kAseRspHdrMinLen, group,
340 leAudioDevice);
341 break;
342 case AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING:
343 AseStateMachineProcessDisabling(arh, ase, group, leAudioDevice);
344 break;
345 case AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING:
346 AseStateMachineProcessReleasing(arh, ase, group, leAudioDevice);
347 break;
348 default:
349 LOG(ERROR) << __func__
350 << ", Wrong AES status: " << static_cast<int>(arh.state);
351 StopStream(group);
352 break;
353 }
354 }
355
ProcessHciNotifOnCigCreate(LeAudioDeviceGroup * group,uint8_t status,uint8_t cig_id,std::vector<uint16_t> conn_handles)356 void ProcessHciNotifOnCigCreate(LeAudioDeviceGroup* group, uint8_t status,
357 uint8_t cig_id,
358 std::vector<uint16_t> conn_handles) override {
359 /* TODO: What if not all cises will be configured ?
360 * conn_handle.size() != active ases in group
361 */
362
363 if (!group) {
364 LOG_ERROR(", group is null");
365 return;
366 }
367
368 if (status != HCI_SUCCESS) {
369 if (status == HCI_ERR_COMMAND_DISALLOWED) {
370 /*
371 * We are here, because stack has no chance to remove CIG when it was
372 * shut during streaming. In the same time, controller probably was not
373 * Reseted, which creates the issue. Lets remove CIG and try to create
374 * it again.
375 */
376 group->SetCigState(CigState::RECOVERING);
377 IsoManager::GetInstance()->RemoveCig(group->group_id_, true);
378 return;
379 }
380
381 group->SetCigState(CigState::NONE);
382 LOG_ERROR(", failed to create CIG, reason: 0x%02x, new cig state: %s",
383 +status, ToString(group->cig_state_).c_str());
384 StopStream(group);
385 return;
386 }
387
388 ASSERT_LOG(group->GetCigState() == CigState::CREATING,
389 "Unexpected CIG creation group id: %d, cig state: %s",
390 group->group_id_, ToString(group->cig_state_).c_str());
391
392 group->SetCigState(CigState::CREATED);
393 LOG_INFO("Group: %p, id: %d cig state: %s, number of cis handles: %d",
394 group, group->group_id_, ToString(group->cig_state_).c_str(),
395 static_cast<int>(conn_handles.size()));
396
397 /* Assign all connection handles to cis ids */
398 group->CigAssignCisConnHandles(conn_handles);
399
400 /* Assign all connection handles to ases */
401 group->CigAssignCisConnHandlesToAses();
402
403 /* Last node configured, process group to codec configured state */
404 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
405
406 if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
407 StartConfigQoSForTheGroup(group);
408 } else {
409 LOG_ERROR(", invalid state transition, from: %s , to: %s",
410 ToString(group->GetState()).c_str(),
411 ToString(group->GetTargetState()).c_str());
412 StopStream(group);
413 return;
414 }
415 }
416
FreeLinkQualityReports(LeAudioDevice * leAudioDevice)417 void FreeLinkQualityReports(LeAudioDevice* leAudioDevice) {
418 if (leAudioDevice->link_quality_timer == nullptr) return;
419
420 alarm_free(leAudioDevice->link_quality_timer);
421 leAudioDevice->link_quality_timer = nullptr;
422 }
423
ProcessHciNotifyOnCigRemoveRecovering(uint8_t status,LeAudioDeviceGroup * group)424 void ProcessHciNotifyOnCigRemoveRecovering(uint8_t status,
425 LeAudioDeviceGroup* group) {
426 group->SetCigState(CigState::NONE);
427
428 if (status != HCI_SUCCESS) {
429 LOG_ERROR(
430 "Could not recover from the COMMAND DISALLOAD on CigCreate. Status "
431 "on CIG remove is 0x%02x",
432 status);
433 StopStream(group);
434 return;
435 }
436 LOG_INFO("Succeed on CIG Recover - back to creating CIG");
437 if (!CigCreate(group)) {
438 LOG_ERROR("Could not create CIG. Stop the stream for group %d",
439 group->group_id_);
440 StopStream(group);
441 }
442 }
443
ProcessHciNotifOnCigRemove(uint8_t status,LeAudioDeviceGroup * group)444 void ProcessHciNotifOnCigRemove(uint8_t status,
445 LeAudioDeviceGroup* group) override {
446 if (group->GetCigState() == CigState::RECOVERING) {
447 ProcessHciNotifyOnCigRemoveRecovering(status, group);
448 return;
449 }
450
451 if (status != HCI_SUCCESS) {
452 group->SetCigState(CigState::CREATED);
453 LOG_ERROR(
454 "failed to remove cig, id: %d, status 0x%02x, new cig state: %s",
455 group->group_id_, +status, ToString(group->GetCigState()).c_str());
456 return;
457 }
458
459 ASSERT_LOG(group->GetCigState() == CigState::REMOVING,
460 "Unexpected CIG remove group id: %d, cig state %s",
461 group->group_id_, ToString(group->GetCigState()).c_str());
462
463 group->SetCigState(CigState::NONE);
464
465 LeAudioDevice* leAudioDevice = group->GetFirstDevice();
466 if (!leAudioDevice) return;
467
468 do {
469 FreeLinkQualityReports(leAudioDevice);
470
471 for (auto& ase : leAudioDevice->ases_) {
472 ase.data_path_state = AudioStreamDataPathState::IDLE;
473 }
474 } while ((leAudioDevice = group->GetNextDevice(leAudioDevice)));
475 }
476
ProcessHciNotifSetupIsoDataPath(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice,uint8_t status,uint16_t conn_handle)477 void ProcessHciNotifSetupIsoDataPath(LeAudioDeviceGroup* group,
478 LeAudioDevice* leAudioDevice,
479 uint8_t status,
480 uint16_t conn_handle) override {
481 if (status) {
482 LOG(ERROR) << __func__ << ", failed to setup data path";
483 StopStream(group);
484
485 return;
486 }
487
488 /* Update state for the given cis.*/
489 auto ase = leAudioDevice->GetFirstActiveAseByDataPathState(
490 AudioStreamDataPathState::CIS_ESTABLISHED);
491
492 if (ase->cis_conn_hdl != conn_handle) {
493 LOG(ERROR) << __func__ << " Cannot find ase by handle " << +conn_handle;
494 return;
495 }
496
497 ase->data_path_state = AudioStreamDataPathState::DATA_PATH_ESTABLISHED;
498
499 if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
500 LOG(WARNING) << __func__ << " Group " << group->group_id_
501 << " is not targeting streaming state any more";
502 return;
503 }
504
505 AddCisToStreamConfiguration(group, ase);
506
507 ase = leAudioDevice->GetFirstActiveAseByDataPathState(
508 AudioStreamDataPathState::CIS_ESTABLISHED);
509 if (!ase) {
510 leAudioDevice = group->GetNextActiveDeviceByDataPathState(
511 leAudioDevice, AudioStreamDataPathState::CIS_ESTABLISHED);
512
513 if (!leAudioDevice) {
514 state_machine_callbacks_->StatusReportCb(group->group_id_,
515 GroupStreamStatus::STREAMING);
516 return;
517 }
518
519 ase = leAudioDevice->GetFirstActiveAseByDataPathState(
520 AudioStreamDataPathState::CIS_ESTABLISHED);
521 }
522
523 ASSERT_LOG(ase, "shouldn't be called without an active ASE");
524 PrepareDataPath(ase);
525 }
526
ProcessHciNotifRemoveIsoDataPath(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice,uint8_t status,uint16_t conn_hdl)527 void ProcessHciNotifRemoveIsoDataPath(LeAudioDeviceGroup* group,
528 LeAudioDevice* leAudioDevice,
529 uint8_t status,
530 uint16_t conn_hdl) override {
531 if (status != HCI_SUCCESS) {
532 LOG_ERROR(
533 "failed to remove ISO data path, reason: 0x%0x - contining stream "
534 "closing",
535 status);
536 /* Just continue - disconnecting CIS removes data path as well.*/
537 }
538
539 bool do_disconnect = false;
540
541 auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(conn_hdl);
542 if (ases_pair.sink && (ases_pair.sink->data_path_state ==
543 AudioStreamDataPathState::DATA_PATH_ESTABLISHED)) {
544 ases_pair.sink->data_path_state =
545 AudioStreamDataPathState::CIS_DISCONNECTING;
546 do_disconnect = true;
547 }
548
549 if (ases_pair.source &&
550 ases_pair.source->data_path_state ==
551 AudioStreamDataPathState::DATA_PATH_ESTABLISHED) {
552 ases_pair.source->data_path_state =
553 AudioStreamDataPathState::CIS_DISCONNECTING;
554 do_disconnect = true;
555 }
556
557 if (do_disconnect) {
558 RemoveCisFromStreamConfiguration(group, leAudioDevice, conn_hdl);
559 IsoManager::GetInstance()->DisconnectCis(conn_hdl, HCI_ERR_PEER_USER);
560 }
561 }
562
ProcessHciNotifIsoLinkQualityRead(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice,uint8_t conn_handle,uint32_t txUnackedPackets,uint32_t txFlushedPackets,uint32_t txLastSubeventPackets,uint32_t retransmittedPackets,uint32_t crcErrorPackets,uint32_t rxUnreceivedPackets,uint32_t duplicatePackets)563 void ProcessHciNotifIsoLinkQualityRead(
564 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice,
565 uint8_t conn_handle, uint32_t txUnackedPackets, uint32_t txFlushedPackets,
566 uint32_t txLastSubeventPackets, uint32_t retransmittedPackets,
567 uint32_t crcErrorPackets, uint32_t rxUnreceivedPackets,
568 uint32_t duplicatePackets) {
569 LOG(INFO) << "conn_handle: " << loghex(conn_handle)
570 << ", txUnackedPackets: " << loghex(txUnackedPackets)
571 << ", txFlushedPackets: " << loghex(txFlushedPackets)
572 << ", txLastSubeventPackets: " << loghex(txLastSubeventPackets)
573 << ", retransmittedPackets: " << loghex(retransmittedPackets)
574 << ", crcErrorPackets: " << loghex(crcErrorPackets)
575 << ", rxUnreceivedPackets: " << loghex(rxUnreceivedPackets)
576 << ", duplicatePackets: " << loghex(duplicatePackets);
577 }
578
ReleaseCisIds(LeAudioDeviceGroup * group)579 void ReleaseCisIds(LeAudioDeviceGroup* group) {
580 if (group == nullptr) {
581 LOG_DEBUG(" Group is null.");
582 return;
583 }
584 LOG_DEBUG(" Releasing CIS is for group %d", group->group_id_);
585
586 LeAudioDevice* leAudioDevice = group->GetFirstDevice();
587 while (leAudioDevice != nullptr) {
588 for (auto& ase : leAudioDevice->ases_) {
589 ase.cis_id = le_audio::kInvalidCisId;
590 ase.cis_conn_hdl = 0;
591 }
592 leAudioDevice = group->GetNextDevice(leAudioDevice);
593 }
594
595 group->CigClearCis();
596 }
597
RemoveCigForGroup(LeAudioDeviceGroup * group)598 void RemoveCigForGroup(LeAudioDeviceGroup* group) {
599 LOG_DEBUG("Group: %p, id: %d cig state: %s", group, group->group_id_,
600 ToString(group->cig_state_).c_str());
601 if (group->GetCigState() != CigState::CREATED) {
602 LOG_WARN("Group: %p, id: %d cig state: %s cannot be removed", group,
603 group->group_id_, ToString(group->cig_state_).c_str());
604 return;
605 }
606
607 group->SetCigState(CigState::REMOVING);
608 IsoManager::GetInstance()->RemoveCig(group->group_id_);
609 LOG_DEBUG("Group: %p, id: %d cig state: %s", group, group->group_id_,
610 ToString(group->cig_state_).c_str());
611 }
612
ProcessHciNotifAclDisconnected(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)613 void ProcessHciNotifAclDisconnected(LeAudioDeviceGroup* group,
614 LeAudioDevice* leAudioDevice) {
615 FreeLinkQualityReports(leAudioDevice);
616 leAudioDevice->conn_id_ = GATT_INVALID_CONN_ID;
617 /* mark ASEs as not used. */
618 leAudioDevice->DeactivateAllAses();
619
620 if (!group) {
621 LOG(ERROR) << __func__
622 << " group is null for device: " << leAudioDevice->address_
623 << " group_id: " << leAudioDevice->group_id_;
624 return;
625 }
626
627 /* If group is in Idle and not transitioning, just update the current group
628 * audio context availability which could change due to disconnected group
629 * member.
630 */
631 if ((group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) &&
632 !group->IsInTransition()) {
633 LOG(INFO) << __func__ << " group: " << group->group_id_ << " is in IDLE";
634 group->UpdateAudioContextTypeAvailability();
635 return;
636 }
637
638 LOG_DEBUG(
639 " device: %s, group connected: %d, all active ase disconnected:: %d",
640 leAudioDevice->address_.ToString().c_str(),
641 group->IsAnyDeviceConnected(), group->HaveAllCisesDisconnected());
642
643 /* Update the current group audio context availability which could change
644 * due to disconnected group member.
645 */
646 group->UpdateAudioContextTypeAvailability();
647
648 /* ACL of one of the device has been dropped.
649 * If there is active CIS, do nothing here. Just update the available
650 * contexts table.
651 */
652 if (group->IsAnyDeviceConnected() && !group->HaveAllCisesDisconnected()) {
653 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
654 /* We keep streaming but want others to let know user that it might be
655 * need to update offloader with new CIS configuration
656 */
657 state_machine_callbacks_->StatusReportCb(group->group_id_,
658 GroupStreamStatus::STREAMING);
659 }
660 return;
661 }
662
663 /* Group is not connected and all the CISes are down.
664 * Clean states and destroy HCI group
665 */
666 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
667 group->SetTargetState(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
668
669 /* Clear group pending status */
670 group->ClearPendingAvailableContextsChange();
671 group->ClearPendingConfiguration();
672
673 if (alarm_is_scheduled(watchdog_)) alarm_cancel(watchdog_);
674 ReleaseCisIds(group);
675 state_machine_callbacks_->StatusReportCb(group->group_id_,
676 GroupStreamStatus::IDLE);
677 RemoveCigForGroup(group);
678 }
679
ProcessHciNotifCisEstablished(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice,const bluetooth::hci::iso_manager::cis_establish_cmpl_evt * event)680 void ProcessHciNotifCisEstablished(
681 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice,
682 const bluetooth::hci::iso_manager::cis_establish_cmpl_evt* event)
683 override {
684 auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(event->cis_conn_hdl);
685
686 if (event->status) {
687 if (ases_pair.sink)
688 ases_pair.sink->data_path_state =
689 AudioStreamDataPathState::CIS_ASSIGNED;
690 if (ases_pair.source)
691 ases_pair.source->data_path_state =
692 AudioStreamDataPathState::CIS_ASSIGNED;
693
694 /* CIS establishment failed. Remove CIG if no other CIS is already created
695 * or pending. If CIS is established, this will be handled in disconnected
696 * complete event
697 */
698 if (group->HaveAllCisesDisconnected()) {
699 RemoveCigForGroup(group);
700 }
701
702 LOG(ERROR) << __func__
703 << ", failed to create CIS, status: " << loghex(event->status);
704
705 StopStream(group);
706 return;
707 }
708
709 if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
710 LOG(ERROR) << __func__
711 << ", Unintended CIS establishement event came for group id:"
712 << group->group_id_;
713 StopStream(group);
714 return;
715 }
716
717 if (ases_pair.sink)
718 ases_pair.sink->data_path_state =
719 AudioStreamDataPathState::CIS_ESTABLISHED;
720 if (ases_pair.source)
721 ases_pair.source->data_path_state =
722 AudioStreamDataPathState::CIS_ESTABLISHED;
723
724 if (osi_property_get_bool("persist.bluetooth.iso_link_quality_report",
725 false)) {
726 leAudioDevice->link_quality_timer =
727 alarm_new_periodic("le_audio_cis_link_quality");
728 leAudioDevice->link_quality_timer_data = event->cis_conn_hdl;
729 alarm_set_on_mloop(leAudioDevice->link_quality_timer,
730 linkQualityCheckInterval, link_quality_cb,
731 &leAudioDevice->link_quality_timer_data);
732 }
733
734 if (!leAudioDevice->HaveAllActiveAsesCisEst()) {
735 /* More cis established events has to come */
736 return;
737 }
738
739 if (!leAudioDevice->IsReadyToCreateStream()) {
740 /* Device still remains in ready to create stream state. It means that
741 * more enabling status notifications has to come. This may only happen
742 * for reconnection scenario for bi-directional CIS.
743 */
744 return;
745 }
746
747 /* All CISes created. Send start ready for source ASE before we can go
748 * to streaming state.
749 */
750 struct ase* ase = leAudioDevice->GetFirstActiveAse();
751 ASSERT_LOG(ase != nullptr,
752 "shouldn't be called without an active ASE, device %s, group "
753 "id: %d, cis handle 0x%04x",
754 leAudioDevice->address_.ToString().c_str(), event->cig_id,
755 event->cis_conn_hdl);
756
757 PrepareAndSendReceiverStartReady(leAudioDevice, ase);
758
759 /* Cis establishment may came after setting group state to streaming, e.g.
760 * for autonomous scenario when ase is sink */
761 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING &&
762 group->IsGroupStreamReady()) {
763 /* No more transition for group */
764 alarm_cancel(watchdog_);
765 PrepareDataPath(group);
766 }
767 }
768
RemoveDataPathByCisHandle(LeAudioDevice * leAudioDevice,uint16_t cis_conn_hdl)769 static void RemoveDataPathByCisHandle(LeAudioDevice* leAudioDevice,
770 uint16_t cis_conn_hdl) {
771 auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl);
772 uint8_t value = 0;
773
774 if (ases_pair.sink && ases_pair.sink->data_path_state ==
775 AudioStreamDataPathState::DATA_PATH_ESTABLISHED) {
776 value |= bluetooth::hci::iso_manager::kRemoveIsoDataPathDirectionInput;
777 }
778
779 if (ases_pair.source &&
780 ases_pair.source->data_path_state ==
781 AudioStreamDataPathState::DATA_PATH_ESTABLISHED) {
782 value |= bluetooth::hci::iso_manager::kRemoveIsoDataPathDirectionOutput;
783 }
784
785 if (value == 0) {
786 LOG_INFO("Data path was not set. Nothing to do here.");
787 return;
788 }
789
790 IsoManager::GetInstance()->RemoveIsoDataPath(cis_conn_hdl, value);
791 }
792
ProcessHciNotifCisDisconnected(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice,const bluetooth::hci::iso_manager::cis_disconnected_evt * event)793 void ProcessHciNotifCisDisconnected(
794 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice,
795 const bluetooth::hci::iso_manager::cis_disconnected_evt* event) override {
796 /* Reset the disconnected CIS states */
797
798 FreeLinkQualityReports(leAudioDevice);
799
800 auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(event->cis_conn_hdl);
801
802 /* If this is peer disconnecting CIS, make sure to clear data path */
803 if (event->reason != HCI_ERR_CONN_CAUSE_LOCAL_HOST) {
804 RemoveDataPathByCisHandle(leAudioDevice, event->cis_conn_hdl);
805 // Make sure we won't stay in STREAMING state
806 if (ases_pair.sink &&
807 ases_pair.sink->state == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
808 ases_pair.sink->state =
809 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
810 }
811 if (ases_pair.source && ases_pair.source->state ==
812 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
813 ases_pair.source->state =
814 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
815 }
816 }
817
818 if (ases_pair.sink) {
819 ases_pair.sink->data_path_state = AudioStreamDataPathState::CIS_ASSIGNED;
820 }
821 if (ases_pair.source) {
822 ases_pair.source->data_path_state =
823 AudioStreamDataPathState::CIS_ASSIGNED;
824 }
825
826 RemoveCisFromStreamConfiguration(group, leAudioDevice, event->cis_conn_hdl);
827
828 auto target_state = group->GetTargetState();
829 switch (target_state) {
830 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
831 /* Something wrong happen when streaming or when creating stream.
832 * If there is other device connected and streaming, just leave it as it
833 * is, otherwise stop the stream.
834 */
835 if (!group->HaveAllCisesDisconnected()) {
836 /* There is ASE streaming for some device. Continue streaming. */
837 LOG_WARN(
838 "Group member disconnected during streaming. Cis handle 0x%04x",
839 event->cis_conn_hdl);
840 return;
841 }
842
843 LOG_INFO("Lost all members from the group %d", group->group_id_);
844 group->cises_.clear();
845 RemoveCigForGroup(group);
846
847 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
848 group->SetTargetState(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
849 /* If there is no more ase to stream. Notify it is in IDLE. */
850 state_machine_callbacks_->StatusReportCb(group->group_id_,
851 GroupStreamStatus::IDLE);
852 return;
853
854 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
855 /* Intentional group disconnect has finished, but the last CIS in the
856 * event came after the ASE notification.
857 * If group is already suspended and all CIS are disconnected, we can
858 * report SUSPENDED state.
859 */
860 if ((group->GetState() ==
861 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) &&
862 group->HaveAllCisesDisconnected()) {
863 /* No more transition for group */
864 alarm_cancel(watchdog_);
865
866 state_machine_callbacks_->StatusReportCb(
867 group->group_id_, GroupStreamStatus::SUSPENDED);
868 return;
869 }
870 break;
871 case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
872 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED: {
873 /* Those two are used when closing the stream and CIS disconnection is
874 * expected */
875 if (!group->HaveAllCisesDisconnected()) {
876 LOG_DEBUG(
877 "Still waiting for all CISes being disconnected for group:%d",
878 group->group_id_);
879 return;
880 }
881
882 auto current_group_state = group->GetState();
883 LOG_INFO("group %d current state: %s, target state: %s",
884 group->group_id_,
885 bluetooth::common::ToString(current_group_state).c_str(),
886 bluetooth::common::ToString(target_state).c_str());
887 /* It might happen that controller notified about CIS disconnection
888 * later, after ASE state already changed.
889 * In such an event, there is need to notify upper layer about state
890 * from here.
891 */
892 if (alarm_is_scheduled(watchdog_)) {
893 alarm_cancel(watchdog_);
894 }
895
896 if (current_group_state == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
897 LOG_INFO(
898 "Cises disconnected for group %d, we are good in Idle state.",
899 group->group_id_);
900 ReleaseCisIds(group);
901 state_machine_callbacks_->StatusReportCb(group->group_id_,
902 GroupStreamStatus::IDLE);
903 } else if (current_group_state ==
904 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED) {
905 auto reconfig = group->IsPendingConfiguration();
906 LOG_INFO(
907 "Cises disconnected for group: %d, we are good in Configured "
908 "state, reconfig=%d.",
909 group->group_id_, reconfig);
910
911 if (reconfig) {
912 group->ClearPendingConfiguration();
913 state_machine_callbacks_->StatusReportCb(
914 group->group_id_, GroupStreamStatus::CONFIGURED_BY_USER);
915 } else {
916 /* This is Autonomous change if both, target and current state
917 * is CODEC_CONFIGURED
918 */
919 if (target_state == current_group_state) {
920 state_machine_callbacks_->StatusReportCb(
921 group->group_id_, GroupStreamStatus::CONFIGURED_AUTONOMOUS);
922 }
923 }
924 }
925 RemoveCigForGroup(group);
926 } break;
927 default:
928 break;
929 }
930
931 /* We should send Receiver Stop Ready when acting as a source */
932 if (ases_pair.source &&
933 ases_pair.source->state == AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING) {
934 std::vector<uint8_t> ids = {ases_pair.source->id};
935 std::vector<uint8_t> value;
936
937 le_audio::client_parser::ascs::PrepareAseCtpAudioReceiverStopReady(ids,
938 value);
939 BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_,
940 leAudioDevice->ctp_hdls_.val_hdl, value,
941 GATT_WRITE_NO_RSP, NULL, NULL);
942 }
943
944 /* Tear down CIS's data paths within the group */
945 struct ase* ase = leAudioDevice->GetFirstActiveAseByDataPathState(
946 AudioStreamDataPathState::DATA_PATH_ESTABLISHED);
947 if (!ase) {
948 leAudioDevice = group->GetNextActiveDevice(leAudioDevice);
949 /* No more ASEs to disconnect their CISes */
950 if (!leAudioDevice) return;
951
952 ase = leAudioDevice->GetFirstActiveAse();
953 }
954
955 LOG_ASSERT(ase) << __func__ << " shouldn't be called without an active ASE";
956 if (ase->data_path_state ==
957 AudioStreamDataPathState::DATA_PATH_ESTABLISHED) {
958 RemoveDataPathByCisHandle(leAudioDevice, ase->cis_conn_hdl);
959 }
960 }
961
962 private:
963 static constexpr uint64_t kStateTransitionTimeoutMs = 3500;
964 static constexpr char kStateTransitionTimeoutMsProp[] =
965 "persist.bluetooth.leaudio.device.set.state.timeoutms";
966 Callbacks* state_machine_callbacks_;
967 alarm_t* watchdog_;
968
969 /* This callback is called on timeout during transition to target state */
OnStateTransitionTimeout(int group_id)970 void OnStateTransitionTimeout(int group_id) {
971 state_machine_callbacks_->OnStateTransitionTimeout(group_id);
972 }
973
SetTargetState(LeAudioDeviceGroup * group,AseState state)974 void SetTargetState(LeAudioDeviceGroup* group, AseState state) {
975 LOG_DEBUG("Watchdog watch started for group=%d transition from %s to %s",
976 group->group_id_, ToString(group->GetTargetState()).c_str(),
977 ToString(state).c_str());
978
979 group->SetTargetState(state);
980
981 /* Group should tie in time to get requested status */
982 uint64_t timeoutMs = kStateTransitionTimeoutMs;
983 timeoutMs =
984 osi_property_get_int32(kStateTransitionTimeoutMsProp, timeoutMs);
985
986 if (alarm_is_scheduled(watchdog_)) alarm_cancel(watchdog_);
987
988 alarm_set_on_mloop(
989 watchdog_, timeoutMs,
990 [](void* data) {
991 if (instance) instance->OnStateTransitionTimeout(PTR_TO_INT(data));
992 },
993 INT_TO_PTR(group->group_id_));
994 }
995
AddCisToStreamConfiguration(LeAudioDeviceGroup * group,const struct ase * ase)996 void AddCisToStreamConfiguration(LeAudioDeviceGroup* group,
997 const struct ase* ase) {
998 uint16_t cis_conn_hdl = ase->cis_conn_hdl;
999 LOG_INFO("Adding cis handle 0x%04x (%s) to stream list", cis_conn_hdl,
1000 ase->direction == le_audio::types::kLeAudioDirectionSink
1001 ? "sink"
1002 : "source");
1003 auto* stream_conf = &group->stream_conf;
1004 if (ase->direction == le_audio::types::kLeAudioDirectionSink) {
1005 auto iter = std::find_if(
1006 stream_conf->sink_streams.begin(), stream_conf->sink_streams.end(),
1007 [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; });
1008
1009 ASSERT_LOG(iter == stream_conf->sink_streams.end(),
1010 "Stream is already there 0x%04x", cis_conn_hdl);
1011
1012 stream_conf->sink_streams.emplace_back(std::make_pair(
1013 ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation));
1014
1015 stream_conf->sink_num_of_devices++;
1016 stream_conf->sink_num_of_channels += ase->codec_config.channel_count;
1017 stream_conf->sink_audio_channel_allocation |=
1018 *ase->codec_config.audio_channel_allocation;
1019
1020 if (stream_conf->sink_sample_frequency_hz == 0) {
1021 stream_conf->sink_sample_frequency_hz =
1022 ase->codec_config.GetSamplingFrequencyHz();
1023 } else {
1024 ASSERT_LOG(stream_conf->sink_sample_frequency_hz ==
1025 ase->codec_config.GetSamplingFrequencyHz(),
1026 "sample freq mismatch: %d!=%d",
1027 stream_conf->sink_sample_frequency_hz,
1028 ase->codec_config.GetSamplingFrequencyHz());
1029 }
1030
1031 if (stream_conf->sink_octets_per_codec_frame == 0) {
1032 stream_conf->sink_octets_per_codec_frame =
1033 *ase->codec_config.octets_per_codec_frame;
1034 } else {
1035 ASSERT_LOG(stream_conf->sink_octets_per_codec_frame ==
1036 *ase->codec_config.octets_per_codec_frame,
1037 "octets per frame mismatch: %d!=%d",
1038 stream_conf->sink_octets_per_codec_frame,
1039 *ase->codec_config.octets_per_codec_frame);
1040 }
1041
1042 if (stream_conf->sink_codec_frames_blocks_per_sdu == 0) {
1043 stream_conf->sink_codec_frames_blocks_per_sdu =
1044 *ase->codec_config.codec_frames_blocks_per_sdu;
1045 } else {
1046 ASSERT_LOG(stream_conf->sink_codec_frames_blocks_per_sdu ==
1047 *ase->codec_config.codec_frames_blocks_per_sdu,
1048 "codec_frames_blocks_per_sdu: %d!=%d",
1049 stream_conf->sink_codec_frames_blocks_per_sdu,
1050 *ase->codec_config.codec_frames_blocks_per_sdu);
1051 }
1052
1053 if (stream_conf->sink_frame_duration_us == 0) {
1054 stream_conf->sink_frame_duration_us =
1055 ase->codec_config.GetFrameDurationUs();
1056 } else {
1057 ASSERT_LOG(stream_conf->sink_frame_duration_us ==
1058 ase->codec_config.GetFrameDurationUs(),
1059 "frame_duration_us: %d!=%d",
1060 stream_conf->sink_frame_duration_us,
1061 ase->codec_config.GetFrameDurationUs());
1062 }
1063
1064 LOG_INFO(
1065 " Added Sink Stream Configuration. CIS Connection Handle: %d"
1066 ", Audio Channel Allocation: %d"
1067 ", Sink Number Of Devices: %d"
1068 ", Sink Number Of Channels: %d",
1069 ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation,
1070 stream_conf->sink_num_of_devices, stream_conf->sink_num_of_channels);
1071
1072 } else {
1073 /* Source case */
1074 auto iter = std::find_if(
1075 stream_conf->source_streams.begin(),
1076 stream_conf->source_streams.end(),
1077 [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; });
1078
1079 ASSERT_LOG(iter == stream_conf->source_streams.end(),
1080 "Stream is already there 0x%04x", cis_conn_hdl);
1081
1082 stream_conf->source_streams.emplace_back(std::make_pair(
1083 ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation));
1084
1085 stream_conf->source_num_of_devices++;
1086 stream_conf->source_num_of_channels += ase->codec_config.channel_count;
1087 stream_conf->source_audio_channel_allocation |=
1088 *ase->codec_config.audio_channel_allocation;
1089
1090 if (stream_conf->source_sample_frequency_hz == 0) {
1091 stream_conf->source_sample_frequency_hz =
1092 ase->codec_config.GetSamplingFrequencyHz();
1093 } else {
1094 ASSERT_LOG(stream_conf->source_sample_frequency_hz ==
1095 ase->codec_config.GetSamplingFrequencyHz(),
1096 "sample freq mismatch: %d!=%d",
1097 stream_conf->source_sample_frequency_hz,
1098 ase->codec_config.GetSamplingFrequencyHz());
1099 }
1100
1101 if (stream_conf->source_octets_per_codec_frame == 0) {
1102 stream_conf->source_octets_per_codec_frame =
1103 *ase->codec_config.octets_per_codec_frame;
1104 } else {
1105 ASSERT_LOG(stream_conf->source_octets_per_codec_frame ==
1106 *ase->codec_config.octets_per_codec_frame,
1107 "octets per frame mismatch: %d!=%d",
1108 stream_conf->source_octets_per_codec_frame,
1109 *ase->codec_config.octets_per_codec_frame);
1110 }
1111
1112 if (stream_conf->source_codec_frames_blocks_per_sdu == 0) {
1113 stream_conf->source_codec_frames_blocks_per_sdu =
1114 *ase->codec_config.codec_frames_blocks_per_sdu;
1115 } else {
1116 ASSERT_LOG(stream_conf->source_codec_frames_blocks_per_sdu ==
1117 *ase->codec_config.codec_frames_blocks_per_sdu,
1118 "codec_frames_blocks_per_sdu: %d!=%d",
1119 stream_conf->source_codec_frames_blocks_per_sdu,
1120 *ase->codec_config.codec_frames_blocks_per_sdu);
1121 }
1122
1123 if (stream_conf->source_frame_duration_us == 0) {
1124 stream_conf->source_frame_duration_us =
1125 ase->codec_config.GetFrameDurationUs();
1126 } else {
1127 ASSERT_LOG(stream_conf->source_frame_duration_us ==
1128 ase->codec_config.GetFrameDurationUs(),
1129 "frame_duration_us: %d!=%d",
1130 stream_conf->source_frame_duration_us,
1131 ase->codec_config.GetFrameDurationUs());
1132 }
1133
1134 LOG_INFO(
1135 " Added Source Stream Configuration. CIS Connection Handle: %d"
1136 ", Audio Channel Allocation: %d"
1137 ", Source Number Of Devices: %d"
1138 ", Source Number Of Channels: %d",
1139 ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation,
1140 stream_conf->source_num_of_devices,
1141 stream_conf->source_num_of_channels);
1142 }
1143
1144 /* Update offloader streams */
1145 group->CreateStreamVectorForOffloader(ase->direction);
1146 }
1147
RemoveCisFromStreamConfiguration(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice,uint16_t cis_conn_hdl)1148 void RemoveCisFromStreamConfiguration(LeAudioDeviceGroup* group,
1149 LeAudioDevice* leAudioDevice,
1150 uint16_t cis_conn_hdl) {
1151 auto* stream_conf = &group->stream_conf;
1152
1153 LOG_INFO(" CIS Connection Handle: %d", cis_conn_hdl);
1154
1155 auto sink_channels = stream_conf->sink_num_of_channels;
1156 auto source_channels = stream_conf->source_num_of_channels;
1157
1158 if (!stream_conf->sink_streams.empty() ||
1159 !stream_conf->source_streams.empty()) {
1160 stream_conf->sink_streams.erase(
1161 std::remove_if(
1162 stream_conf->sink_streams.begin(),
1163 stream_conf->sink_streams.end(),
1164 [leAudioDevice, &cis_conn_hdl, &stream_conf](auto& pair) {
1165 if (!cis_conn_hdl) {
1166 cis_conn_hdl = pair.first;
1167 }
1168 auto ases_pair =
1169 leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl);
1170 if (ases_pair.sink && cis_conn_hdl == pair.first) {
1171 stream_conf->sink_num_of_devices--;
1172 stream_conf->sink_num_of_channels -=
1173 ases_pair.sink->codec_config.channel_count;
1174 stream_conf->sink_audio_channel_allocation &= ~pair.second;
1175 }
1176 return (ases_pair.sink && cis_conn_hdl == pair.first);
1177 }),
1178 stream_conf->sink_streams.end());
1179
1180 stream_conf->source_streams.erase(
1181 std::remove_if(
1182 stream_conf->source_streams.begin(),
1183 stream_conf->source_streams.end(),
1184 [leAudioDevice, &cis_conn_hdl, &stream_conf](auto& pair) {
1185 if (!cis_conn_hdl) {
1186 cis_conn_hdl = pair.first;
1187 }
1188 auto ases_pair =
1189 leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl);
1190 if (ases_pair.source && cis_conn_hdl == pair.first) {
1191 stream_conf->source_num_of_devices--;
1192 stream_conf->source_num_of_channels -=
1193 ases_pair.source->codec_config.channel_count;
1194 stream_conf->source_audio_channel_allocation &= ~pair.second;
1195 }
1196 return (ases_pair.source && cis_conn_hdl == pair.first);
1197 }),
1198 stream_conf->source_streams.end());
1199
1200 LOG_INFO(
1201 " Sink Number Of Devices: %d"
1202 ", Sink Number Of Channels: %d"
1203 ", Source Number Of Devices: %d"
1204 ", Source Number Of Channels: %d",
1205 stream_conf->sink_num_of_devices, stream_conf->sink_num_of_channels,
1206 stream_conf->source_num_of_devices,
1207 stream_conf->source_num_of_channels);
1208 }
1209
1210 if (stream_conf->sink_num_of_channels == 0) {
1211 group->ClearSinksFromConfiguration();
1212 }
1213
1214 if (stream_conf->source_num_of_channels == 0) {
1215 group->ClearSourcesFromConfiguration();
1216 }
1217
1218 /* Update offloader streams if needed */
1219 if (sink_channels > stream_conf->sink_num_of_channels) {
1220 group->CreateStreamVectorForOffloader(
1221 le_audio::types::kLeAudioDirectionSink);
1222 }
1223 if (source_channels > stream_conf->source_num_of_channels) {
1224 group->CreateStreamVectorForOffloader(
1225 le_audio::types::kLeAudioDirectionSource);
1226 }
1227
1228 group->CigUnassignCis(leAudioDevice);
1229 }
1230
CigCreate(LeAudioDeviceGroup * group)1231 bool CigCreate(LeAudioDeviceGroup* group) {
1232 uint32_t sdu_interval_mtos, sdu_interval_stom;
1233 uint16_t max_trans_lat_mtos, max_trans_lat_stom;
1234 uint8_t packing, framing, sca;
1235 std::vector<EXT_CIS_CFG> cis_cfgs;
1236
1237 LOG_DEBUG("Group: %p, id: %d cig state: %s", group, group->group_id_,
1238 ToString(group->cig_state_).c_str());
1239
1240 if (group->GetCigState() != CigState::NONE) {
1241 LOG_WARN(" Group %p, id: %d has invalid cig state: %s ", group,
1242 group->group_id_, ToString(group->cig_state_).c_str());
1243 return false;
1244 }
1245
1246 sdu_interval_mtos =
1247 group->GetSduInterval(le_audio::types::kLeAudioDirectionSink);
1248 sdu_interval_stom =
1249 group->GetSduInterval(le_audio::types::kLeAudioDirectionSource);
1250 sca = group->GetSCA();
1251 packing = group->GetPacking();
1252 framing = group->GetFraming();
1253 max_trans_lat_mtos = group->GetMaxTransportLatencyMtos();
1254 max_trans_lat_stom = group->GetMaxTransportLatencyStom();
1255
1256 uint16_t max_sdu_size_mtos = 0;
1257 uint16_t max_sdu_size_stom = 0;
1258 uint8_t phy_mtos =
1259 group->GetPhyBitmask(le_audio::types::kLeAudioDirectionSink);
1260 uint8_t phy_stom =
1261 group->GetPhyBitmask(le_audio::types::kLeAudioDirectionSource);
1262 uint8_t rtn_mtos = 0;
1263 uint8_t rtn_stom = 0;
1264
1265 /* Currently assumed Sink/Source configuration is same across cis types.
1266 * If a cis in cises_ is currently associated with active device/ASE(s),
1267 * use the Sink/Source configuration for the same.
1268 * If a cis in cises_ is not currently associated with active device/ASE(s),
1269 * use the Sink/Source configuration for the cis in cises_
1270 * associated with a active device/ASE(s). When the same cis is associated
1271 * later, with active device/ASE(s), check if current configuration is
1272 * supported or not, if not, reconfigure CIG.
1273 */
1274 for (struct le_audio::types::cis& cis : group->cises_) {
1275 uint16_t max_sdu_size_mtos_temp =
1276 group->GetMaxSduSize(le_audio::types::kLeAudioDirectionSink, cis.id);
1277 uint16_t max_sdu_size_stom_temp = group->GetMaxSduSize(
1278 le_audio::types::kLeAudioDirectionSource, cis.id);
1279 uint8_t rtn_mtos_temp =
1280 group->GetRtn(le_audio::types::kLeAudioDirectionSink, cis.id);
1281 uint8_t rtn_stom_temp =
1282 group->GetRtn(le_audio::types::kLeAudioDirectionSource, cis.id);
1283
1284 max_sdu_size_mtos =
1285 max_sdu_size_mtos_temp ? max_sdu_size_mtos_temp : max_sdu_size_mtos;
1286 max_sdu_size_stom =
1287 max_sdu_size_stom_temp ? max_sdu_size_stom_temp : max_sdu_size_stom;
1288 rtn_mtos = rtn_mtos_temp ? rtn_mtos_temp : rtn_mtos;
1289 rtn_stom = rtn_stom_temp ? rtn_stom_temp : rtn_stom;
1290 }
1291
1292 for (struct le_audio::types::cis& cis : group->cises_) {
1293 EXT_CIS_CFG cis_cfg = {};
1294
1295 cis_cfg.cis_id = cis.id;
1296 cis_cfg.phy_mtos = phy_mtos;
1297 cis_cfg.phy_stom = phy_stom;
1298 if (cis.type == le_audio::types::CisType::CIS_TYPE_BIDIRECTIONAL) {
1299 cis_cfg.max_sdu_size_mtos = max_sdu_size_mtos;
1300 cis_cfg.rtn_mtos = rtn_mtos;
1301 cis_cfg.max_sdu_size_stom = max_sdu_size_stom;
1302 cis_cfg.rtn_stom = rtn_stom;
1303 cis_cfgs.push_back(cis_cfg);
1304 } else if (cis.type ==
1305 le_audio::types::CisType::CIS_TYPE_UNIDIRECTIONAL_SINK) {
1306 cis_cfg.max_sdu_size_mtos = max_sdu_size_mtos;
1307 cis_cfg.rtn_mtos = rtn_mtos;
1308 cis_cfg.max_sdu_size_stom = 0;
1309 cis_cfg.rtn_stom = 0;
1310 cis_cfgs.push_back(cis_cfg);
1311 } else {
1312 cis_cfg.max_sdu_size_mtos = 0;
1313 cis_cfg.rtn_mtos = 0;
1314 cis_cfg.max_sdu_size_stom = max_sdu_size_stom;
1315 cis_cfg.rtn_stom = rtn_stom;
1316 cis_cfgs.push_back(cis_cfg);
1317 }
1318 }
1319
1320 if ((sdu_interval_mtos == 0 && sdu_interval_stom == 0) ||
1321 (max_trans_lat_mtos == le_audio::types::kMaxTransportLatencyMin &&
1322 max_trans_lat_stom == le_audio::types::kMaxTransportLatencyMin) ||
1323 (max_sdu_size_mtos == 0 && max_sdu_size_stom == 0)) {
1324 LOG_ERROR(" Trying to create invalid group");
1325 group->PrintDebugState();
1326 return false;
1327 }
1328
1329 bluetooth::hci::iso_manager::cig_create_params param = {
1330 .sdu_itv_mtos = sdu_interval_mtos,
1331 .sdu_itv_stom = sdu_interval_stom,
1332 .sca = sca,
1333 .packing = packing,
1334 .framing = framing,
1335 .max_trans_lat_stom = max_trans_lat_stom,
1336 .max_trans_lat_mtos = max_trans_lat_mtos,
1337 .cis_cfgs = std::move(cis_cfgs),
1338 };
1339 group->SetCigState(CigState::CREATING);
1340 IsoManager::GetInstance()->CreateCig(group->group_id_, std::move(param));
1341 LOG_DEBUG("Group: %p, id: %d cig state: %s", group, group->group_id_,
1342 ToString(group->cig_state_).c_str());
1343 return true;
1344 }
1345
CisCreateForDevice(LeAudioDevice * leAudioDevice)1346 static void CisCreateForDevice(LeAudioDevice* leAudioDevice) {
1347 std::vector<EXT_CIS_CREATE_CFG> conn_pairs;
1348 struct ase* ase = leAudioDevice->GetFirstActiveAse();
1349 do {
1350 /* First in ase pair is Sink, second Source */
1351 auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(ase->cis_conn_hdl);
1352
1353 /* Already in pending state - bi-directional CIS */
1354 if (ase->data_path_state == AudioStreamDataPathState::CIS_PENDING)
1355 continue;
1356
1357 if (ases_pair.sink)
1358 ases_pair.sink->data_path_state = AudioStreamDataPathState::CIS_PENDING;
1359 if (ases_pair.source)
1360 ases_pair.source->data_path_state =
1361 AudioStreamDataPathState::CIS_PENDING;
1362
1363 uint16_t acl_handle =
1364 BTM_GetHCIConnHandle(leAudioDevice->address_, BT_TRANSPORT_LE);
1365 conn_pairs.push_back({.cis_conn_handle = ase->cis_conn_hdl,
1366 .acl_conn_handle = acl_handle});
1367 LOG(INFO) << __func__ << " cis handle: " << +ase->cis_conn_hdl
1368 << " acl handle : " << loghex(+acl_handle);
1369
1370 } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
1371
1372 IsoManager::GetInstance()->EstablishCis(
1373 {.conn_pairs = std::move(conn_pairs)});
1374 }
1375
CisCreate(LeAudioDeviceGroup * group)1376 static void CisCreate(LeAudioDeviceGroup* group) {
1377 LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
1378 struct ase* ase;
1379 std::vector<EXT_CIS_CREATE_CFG> conn_pairs;
1380
1381 LOG_ASSERT(leAudioDevice)
1382 << __func__ << " Shouldn't be called without an active device.";
1383
1384 do {
1385 ase = leAudioDevice->GetFirstActiveAse();
1386 LOG_ASSERT(ase) << __func__
1387 << " shouldn't be called without an active ASE";
1388 do {
1389 /* First is ase pair is Sink, second Source */
1390 auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(ase->cis_conn_hdl);
1391
1392 /* Already in pending state - bi-directional CIS */
1393 if (ase->data_path_state == AudioStreamDataPathState::CIS_PENDING)
1394 continue;
1395
1396 if (ases_pair.sink)
1397 ases_pair.sink->data_path_state =
1398 AudioStreamDataPathState::CIS_PENDING;
1399 if (ases_pair.source)
1400 ases_pair.source->data_path_state =
1401 AudioStreamDataPathState::CIS_PENDING;
1402
1403 uint16_t acl_handle =
1404 BTM_GetHCIConnHandle(leAudioDevice->address_, BT_TRANSPORT_LE);
1405 conn_pairs.push_back({.cis_conn_handle = ase->cis_conn_hdl,
1406 .acl_conn_handle = acl_handle});
1407 DLOG(INFO) << __func__ << " cis handle: " << +ase->cis_conn_hdl
1408 << " acl handle : " << loghex(+acl_handle);
1409
1410 } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
1411 } while ((leAudioDevice = group->GetNextActiveDevice(leAudioDevice)));
1412
1413 IsoManager::GetInstance()->EstablishCis(
1414 {.conn_pairs = std::move(conn_pairs)});
1415 }
1416
PrepareDataPath(const struct ase * ase)1417 static void PrepareDataPath(const struct ase* ase) {
1418 /* TODO: Handle HW offloading decode as we handle here, force to use SW
1419 * decode for now */
1420 auto data_path_id = bluetooth::hci::iso_manager::kIsoDataPathHci;
1421 if (CodecManager::GetInstance()->GetCodecLocation() !=
1422 CodecLocation::HOST) {
1423 data_path_id = bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault;
1424 }
1425 /* TODO: Need to set coding_format when we support the codec location inside
1426 * the controller, force to use transparent for now */
1427 bluetooth::hci::iso_manager::iso_data_path_params param = {
1428 .data_path_dir =
1429 ase->direction == le_audio::types::kLeAudioDirectionSink
1430 ? bluetooth::hci::iso_manager::kIsoDataPathDirectionIn
1431 : bluetooth::hci::iso_manager::kIsoDataPathDirectionOut,
1432 .data_path_id = data_path_id,
1433 .codec_id_format = bluetooth::hci::kIsoCodingFormatTransparent,
1434 .codec_id_company = ase->codec_id.vendor_company_id,
1435 .codec_id_vendor = ase->codec_id.vendor_codec_id,
1436 .controller_delay = 0x00000000,
1437 .codec_conf = std::vector<uint8_t>(),
1438 };
1439 IsoManager::GetInstance()->SetupIsoDataPath(ase->cis_conn_hdl,
1440 std::move(param));
1441 }
1442
PrepareDataPath(LeAudioDeviceGroup * group)1443 static inline void PrepareDataPath(LeAudioDeviceGroup* group) {
1444 auto* leAudioDevice = group->GetFirstActiveDeviceByDataPathState(
1445 AudioStreamDataPathState::CIS_ESTABLISHED);
1446 LOG_ASSERT(leAudioDevice)
1447 << __func__ << " Shouldn't be called without an active device.";
1448
1449 auto* ase = leAudioDevice->GetFirstActiveAseByDataPathState(
1450 AudioStreamDataPathState::CIS_ESTABLISHED);
1451 LOG_ASSERT(ase) << __func__ << " shouldn't be called without an active ASE";
1452 PrepareDataPath(ase);
1453 }
1454
ReleaseDataPath(LeAudioDeviceGroup * group)1455 static void ReleaseDataPath(LeAudioDeviceGroup* group) {
1456 LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
1457 LOG_ASSERT(leAudioDevice)
1458 << __func__ << " Shouldn't be called without an active device.";
1459
1460 auto ase = leAudioDevice->GetFirstActiveAseByDataPathState(
1461 AudioStreamDataPathState::DATA_PATH_ESTABLISHED);
1462 LOG_ASSERT(ase) << __func__
1463 << " Shouldn't be called without an active ASE.";
1464 RemoveDataPathByCisHandle(leAudioDevice, ase->cis_conn_hdl);
1465 }
1466
AseStateMachineProcessIdle(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)1467 void AseStateMachineProcessIdle(
1468 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
1469 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
1470 switch (ase->state) {
1471 case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
1472 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
1473 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
1474 if (ase->id == 0x00) {
1475 /* Initial state of Ase - update id */
1476 LOG(INFO) << __func__
1477 << ", discovered ase id: " << static_cast<int>(arh.id);
1478 ase->id = arh.id;
1479 }
1480 break;
1481 case AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING: {
1482 LeAudioDevice* leAudioDeviceNext;
1483 ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_IDLE;
1484 ase->active = false;
1485 ase->configured_for_context_type =
1486 le_audio::types::LeAudioContextType::UNINITIALIZED;
1487
1488 if (!leAudioDevice->HaveAllActiveAsesSameState(
1489 AseState::BTA_LE_AUDIO_ASE_STATE_IDLE)) {
1490 /* More ASEs notification from this device has to come for this group
1491 */
1492 LOG_DEBUG("Wait for more ASE to configure for device %s",
1493 leAudioDevice->address_.ToString().c_str());
1494 return;
1495 }
1496
1497 /* Before continue with release, make sure this is what is requested.
1498 * If not (e.g. only single device got disconnected), stop here
1499 */
1500 if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
1501 LOG_DEBUG("Autonomus change of stated for device %s, ase id: %d",
1502 leAudioDevice->address_.ToString().c_str(), ase->id);
1503 return;
1504 }
1505
1506 leAudioDeviceNext = group->GetNextActiveDevice(leAudioDevice);
1507
1508 /* Configure ASEs for next device in group */
1509 if (leAudioDeviceNext) {
1510 PrepareAndSendRelease(leAudioDeviceNext);
1511 } else {
1512 /* Last node is in releasing state*/
1513 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
1514
1515 group->PrintDebugState();
1516 /* If all CISes are disconnected, notify upper layer about IDLE state,
1517 * otherwise wait for */
1518 if (!group->HaveAllCisesDisconnected()) {
1519 LOG_WARN(
1520 "Not all CISes removed before going to IDLE for group %d, "
1521 "waiting...",
1522 group->group_id_);
1523 group->PrintDebugState();
1524 return;
1525 }
1526
1527 if (alarm_is_scheduled(watchdog_)) alarm_cancel(watchdog_);
1528 ReleaseCisIds(group);
1529 state_machine_callbacks_->StatusReportCb(group->group_id_,
1530 GroupStreamStatus::IDLE);
1531 }
1532 break;
1533 }
1534 default:
1535 LOG(ERROR) << __func__ << ", invalid state transition, from: "
1536 << static_cast<int>(ase->state) << ", to: "
1537 << static_cast<int>(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
1538 StopStream(group);
1539 break;
1540 }
1541 }
1542
StartConfigQoSForTheGroup(LeAudioDeviceGroup * group)1543 void StartConfigQoSForTheGroup(LeAudioDeviceGroup* group) {
1544 LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
1545 if (!leAudioDevice) {
1546 LOG(ERROR) << __func__ << ", no active devices in group";
1547 StopStream(group);
1548 return;
1549 }
1550
1551 PrepareAndSendConfigQos(group, leAudioDevice);
1552 }
1553
PrepareAndSendCodecConfigure(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)1554 void PrepareAndSendCodecConfigure(LeAudioDeviceGroup* group,
1555 LeAudioDevice* leAudioDevice) {
1556 struct le_audio::client_parser::ascs::ctp_codec_conf conf;
1557 std::vector<struct le_audio::client_parser::ascs::ctp_codec_conf> confs;
1558 struct ase* ase;
1559
1560 if (!group->CigAssignCisIds(leAudioDevice)) {
1561 LOG_ERROR(" unable to assign CIS IDs");
1562 StopStream(group);
1563 return;
1564 }
1565
1566 if (group->GetCigState() == CigState::CREATED)
1567 group->CigAssignCisConnHandlesToAses(leAudioDevice);
1568
1569 ase = leAudioDevice->GetFirstActiveAse();
1570 ASSERT_LOG(ase, "shouldn't be called without an active ASE");
1571 for (; ase != nullptr; ase = leAudioDevice->GetNextActiveAse(ase)) {
1572 LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
1573 leAudioDevice->address_.ToString().c_str(), ase->id,
1574 ase->cis_id, ToString(ase->state).c_str());
1575 conf.ase_id = ase->id;
1576 conf.target_latency = ase->target_latency;
1577 conf.target_phy = group->GetTargetPhy(ase->direction);
1578 conf.codec_id = ase->codec_id;
1579 conf.codec_config = ase->codec_config;
1580 confs.push_back(conf);
1581 }
1582
1583 std::vector<uint8_t> value;
1584 le_audio::client_parser::ascs::PrepareAseCtpCodecConfig(confs, value);
1585 BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_,
1586 leAudioDevice->ctp_hdls_.val_hdl, value,
1587 GATT_WRITE_NO_RSP, NULL, NULL);
1588 }
1589
AseStateMachineProcessCodecConfigured(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,uint8_t * data,uint16_t len,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)1590 void AseStateMachineProcessCodecConfigured(
1591 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
1592 uint8_t* data, uint16_t len, LeAudioDeviceGroup* group,
1593 LeAudioDevice* leAudioDevice) {
1594 if (!group) {
1595 LOG(ERROR) << __func__ << ", leAudioDevice doesn't belong to any group";
1596
1597 return;
1598 }
1599
1600 /* ase contain current ASE state. New state is in "arh" */
1601 switch (ase->state) {
1602 case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE: {
1603 if (ase->id == 0x00) {
1604 /* Initial state of Ase - update id */
1605 LOG(INFO) << __func__
1606 << ", discovered ase id: " << static_cast<int>(arh.id);
1607 ase->id = arh.id;
1608 }
1609
1610 LeAudioDevice* leAudioDeviceNext;
1611
1612 struct le_audio::client_parser::ascs::ase_codec_configured_state_params
1613 rsp;
1614
1615 /* Cache codec configured status values for further
1616 * configuration/reconfiguration
1617 */
1618 if (!ParseAseStatusCodecConfiguredStateParams(rsp, len, data)) {
1619 StopStream(group);
1620 return;
1621 }
1622
1623 uint16_t cig_curr_max_trans_lat_mtos =
1624 group->GetMaxTransportLatencyMtos();
1625 uint16_t cig_curr_max_trans_lat_stom =
1626 group->GetMaxTransportLatencyStom();
1627
1628 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
1629 /* We are here because of the reconnection of the single device.
1630 * Reconfigure CIG if current CIG supported Max Transport Latency for
1631 * a direction, cannot be supported by the newly connected member
1632 * device's ASE for the direction.
1633 */
1634 if ((ase->direction == le_audio::types::kLeAudioDirectionSink &&
1635 cig_curr_max_trans_lat_mtos > rsp.max_transport_latency) ||
1636 (ase->direction == le_audio::types::kLeAudioDirectionSource &&
1637 cig_curr_max_trans_lat_stom > rsp.max_transport_latency)) {
1638 group->SetPendingConfiguration();
1639 StopStream(group);
1640 return;
1641 }
1642 }
1643
1644 ase->framing = rsp.framing;
1645 ase->preferred_phy = rsp.preferred_phy;
1646 /* Validate and update QoS settings to be consistent */
1647 if ((!ase->max_transport_latency ||
1648 ase->max_transport_latency > rsp.max_transport_latency) ||
1649 !ase->retrans_nb) {
1650 ase->max_transport_latency = rsp.max_transport_latency;
1651 ase->retrans_nb = rsp.preferred_retrans_nb;
1652 LOG_INFO(
1653 " Using server preferred QoS settings. Max Transport Latency: %d"
1654 ", Retransmission Number: %d",
1655 +ase->max_transport_latency, ase->retrans_nb);
1656 }
1657 ase->pres_delay_min = rsp.pres_delay_min;
1658 ase->pres_delay_max = rsp.pres_delay_max;
1659 ase->preferred_pres_delay_min = rsp.preferred_pres_delay_min;
1660 ase->preferred_pres_delay_max = rsp.preferred_pres_delay_max;
1661
1662 ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
1663
1664 if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
1665 /* This is autonomus change of the remote device */
1666 LOG_DEBUG("Autonomus change for device %s, ase id %d. Just store it.",
1667 leAudioDevice->address_.ToString().c_str(), ase->id);
1668 return;
1669 }
1670
1671 if (leAudioDevice->HaveAnyUnconfiguredAses()) {
1672 /* More ASEs notification from this device has to come for this group
1673 */
1674 LOG_DEBUG("More Ases to be configured for the device %s",
1675 leAudioDevice->address_.ToString().c_str());
1676 return;
1677 }
1678
1679 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
1680 /* We are here because of the reconnection of the single device. */
1681 PrepareAndSendConfigQos(group, leAudioDevice);
1682 return;
1683 }
1684
1685 leAudioDeviceNext = group->GetNextActiveDevice(leAudioDevice);
1686
1687 /* Configure ASEs for next device in group */
1688 if (leAudioDeviceNext) {
1689 PrepareAndSendCodecConfigure(group, leAudioDeviceNext);
1690 } else {
1691 /* Last node configured, process group to codec configured state */
1692 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
1693
1694 if (group->GetTargetState() ==
1695 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
1696 if (!CigCreate(group)) {
1697 LOG_ERROR("Could not create CIG. Stop the stream for group %d",
1698 group->group_id_);
1699 StopStream(group);
1700 }
1701 return;
1702 }
1703
1704 if (group->GetTargetState() ==
1705 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED &&
1706 group->IsPendingConfiguration()) {
1707 LOG_INFO(" Configured state completed ");
1708
1709 /* If all CISes are disconnected, notify upper layer about IDLE
1710 * state, otherwise wait for */
1711 if (!group->HaveAllCisesDisconnected()) {
1712 LOG_WARN(
1713 "Not all CISes removed before going to CONFIGURED for group "
1714 "%d, "
1715 "waiting...",
1716 group->group_id_);
1717 group->PrintDebugState();
1718 return;
1719 }
1720
1721 group->ClearPendingConfiguration();
1722 state_machine_callbacks_->StatusReportCb(
1723 group->group_id_, GroupStreamStatus::CONFIGURED_BY_USER);
1724
1725 /* No more transition for group */
1726 alarm_cancel(watchdog_);
1727 return;
1728 }
1729
1730 LOG_ERROR(", invalid state transition, from: %s to %s",
1731 ToString(group->GetState()).c_str(),
1732 ToString(group->GetTargetState()).c_str());
1733 StopStream(group);
1734 return;
1735 }
1736
1737 break;
1738 }
1739 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED: {
1740 /* Received Configured in Configured state. This could be done
1741 * autonomously because of the reconfiguration done by us
1742 */
1743
1744 struct le_audio::client_parser::ascs::ase_codec_configured_state_params
1745 rsp;
1746
1747 /* Cache codec configured status values for further
1748 * configuration/reconfiguration
1749 */
1750 if (!ParseAseStatusCodecConfiguredStateParams(rsp, len, data)) {
1751 StopStream(group);
1752 return;
1753 }
1754
1755 ase->framing = rsp.framing;
1756 ase->preferred_phy = rsp.preferred_phy;
1757 /* Validate and update QoS settings to be consistent */
1758 if ((!ase->max_transport_latency ||
1759 ase->max_transport_latency > rsp.max_transport_latency) ||
1760 !ase->retrans_nb) {
1761 ase->max_transport_latency = rsp.max_transport_latency;
1762 ase->retrans_nb = rsp.preferred_retrans_nb;
1763 LOG(INFO) << __func__ << " Using server preferred QoS settings."
1764 << " Max Transport Latency: " << +ase->max_transport_latency
1765 << ", Retransmission Number: " << +ase->retrans_nb;
1766 }
1767 ase->pres_delay_min = rsp.pres_delay_min;
1768 ase->pres_delay_max = rsp.pres_delay_max;
1769 ase->preferred_pres_delay_min = rsp.preferred_pres_delay_min;
1770 ase->preferred_pres_delay_max = rsp.preferred_pres_delay_max;
1771
1772 /* This may be a notification from a re-configured ASE */
1773 ase->reconfigure = false;
1774
1775 if (leAudioDevice->HaveAnyUnconfiguredAses()) {
1776 /* Waiting for others to be reconfigured */
1777 return;
1778 }
1779
1780 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
1781 /* We are here because of the reconnection of the single device. */
1782 PrepareAndSendConfigQos(group, leAudioDevice);
1783 return;
1784 }
1785
1786 LeAudioDevice* leAudioDeviceNext =
1787 group->GetNextActiveDevice(leAudioDevice);
1788
1789 /* Configure ASEs for next device in group */
1790 if (leAudioDeviceNext) {
1791 PrepareAndSendCodecConfigure(group, leAudioDeviceNext);
1792 } else {
1793 /* Last node configured, process group to codec configured state */
1794 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
1795
1796 if (group->GetTargetState() ==
1797 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
1798 if (!CigCreate(group)) {
1799 LOG_ERROR("Could not create CIG. Stop the stream for group %d",
1800 group->group_id_);
1801 StopStream(group);
1802 }
1803 return;
1804 }
1805
1806 if (group->GetTargetState() ==
1807 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED &&
1808 group->IsPendingConfiguration()) {
1809 LOG_INFO(" Configured state completed ");
1810 group->ClearPendingConfiguration();
1811 state_machine_callbacks_->StatusReportCb(
1812 group->group_id_, GroupStreamStatus::CONFIGURED_BY_USER);
1813
1814 /* No more transition for group */
1815 alarm_cancel(watchdog_);
1816 return;
1817 }
1818
1819 LOG_ERROR(", Autonomouse change, from: %s to %s",
1820 ToString(group->GetState()).c_str(),
1821 ToString(group->GetTargetState()).c_str());
1822 }
1823
1824 break;
1825 }
1826 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
1827 /* TODO: Config Codec */
1828 break;
1829 case AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING:
1830 LeAudioDevice* leAudioDeviceNext;
1831 ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
1832 ase->active = false;
1833
1834 if (!leAudioDevice->HaveAllActiveAsesSameState(
1835 AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED)) {
1836 /* More ASEs notification from this device has to come for this group
1837 */
1838 LOG_DEBUG("Wait for more ASE to configure for device %s",
1839 leAudioDevice->address_.ToString().c_str());
1840 return;
1841 }
1842
1843 /* Before continue with release, make sure this is what is requested.
1844 * If not (e.g. only single device got disconnected), stop here
1845 */
1846 if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
1847 LOG_DEBUG("Autonomus change of stated for device %s, ase id: %d",
1848 leAudioDevice->address_.ToString().c_str(), ase->id);
1849 return;
1850 }
1851
1852 leAudioDeviceNext = group->GetNextActiveDevice(leAudioDevice);
1853
1854 /* Configure ASEs for next device in group */
1855 if (leAudioDeviceNext) {
1856 PrepareAndSendRelease(leAudioDeviceNext);
1857 } else {
1858 /* Last node is in releasing state*/
1859
1860 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
1861 /* Remote device has cache and keep staying in configured state after
1862 * release. Therefore, we assume this is a target state requested by
1863 * remote device.
1864 */
1865 group->SetTargetState(group->GetState());
1866
1867 if (!group->HaveAllCisesDisconnected()) {
1868 LOG_WARN(
1869 "Not all CISes removed before going to IDLE for group %d, "
1870 "waiting...",
1871 group->group_id_);
1872 group->PrintDebugState();
1873 return;
1874 }
1875
1876 if (alarm_is_scheduled(watchdog_)) alarm_cancel(watchdog_);
1877
1878 state_machine_callbacks_->StatusReportCb(
1879 group->group_id_, GroupStreamStatus::CONFIGURED_AUTONOMOUS);
1880 }
1881 break;
1882 default:
1883 LOG(ERROR) << __func__ << ", invalid state transition, from: "
1884 << static_cast<int>(ase->state) << ", to: "
1885 << static_cast<int>(
1886 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
1887 StopStream(group);
1888 break;
1889 }
1890 }
1891
AseStateMachineProcessQosConfigured(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)1892 void AseStateMachineProcessQosConfigured(
1893 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
1894 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
1895 if (!group) {
1896 LOG(ERROR) << __func__ << ", leAudioDevice doesn't belong to any group";
1897
1898 return;
1899 }
1900
1901 switch (ase->state) {
1902 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED: {
1903 ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED;
1904
1905 if (!leAudioDevice->HaveAllActiveAsesSameState(
1906 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED)) {
1907 /* More ASEs notification from this device has to come for this group
1908 */
1909 return;
1910 }
1911
1912 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
1913 /* We are here because of the reconnection of the single device. */
1914 PrepareAndSendEnable(leAudioDevice);
1915 return;
1916 }
1917
1918 LeAudioDevice* leAudioDeviceNext =
1919 group->GetNextActiveDevice(leAudioDevice);
1920
1921 /* Configure ASEs qos for next device in group */
1922 if (leAudioDeviceNext) {
1923 PrepareAndSendConfigQos(group, leAudioDeviceNext);
1924 } else {
1925 leAudioDevice = group->GetFirstActiveDevice();
1926 LOG_ASSERT(leAudioDevice)
1927 << __func__ << " Shouldn't be called without an active device.";
1928 PrepareAndSendEnable(leAudioDevice);
1929 }
1930
1931 break;
1932 }
1933 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
1934 /* TODO: Config Codec error/Config Qos/Config QoS error/Enable error */
1935 break;
1936 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
1937 if (ase->direction == le_audio::types::kLeAudioDirectionSource) {
1938 /* Source ASE cannot go from Streaming to QoS Configured state */
1939 LOG(ERROR) << __func__ << ", invalid state transition, from: "
1940 << static_cast<int>(ase->state) << ", to: "
1941 << static_cast<int>(
1942 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
1943 StopStream(group);
1944 return;
1945 }
1946
1947 ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED;
1948
1949 /* Process the Disable Transition of the rest of group members if no
1950 * more ASE notifications has to come from this device. */
1951 if (leAudioDevice->IsReadyToSuspendStream())
1952 ProcessGroupDisable(group, leAudioDevice);
1953
1954 break;
1955
1956 case AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING: {
1957 ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED;
1958
1959 /* More ASEs notification from this device has to come for this group */
1960 if (!group->HaveAllActiveDevicesAsesTheSameState(
1961 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED))
1962 return;
1963
1964 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
1965
1966 if (!group->HaveAllCisesDisconnected()) return;
1967
1968 if (group->GetTargetState() ==
1969 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) {
1970 /* No more transition for group */
1971 alarm_cancel(watchdog_);
1972
1973 state_machine_callbacks_->StatusReportCb(
1974 group->group_id_, GroupStreamStatus::SUSPENDED);
1975 } else {
1976 LOG_ERROR(", invalid state transition, from: %s, to: %s",
1977 ToString(group->GetState()).c_str(),
1978 ToString(group->GetTargetState()).c_str());
1979 StopStream(group);
1980 return;
1981 }
1982 break;
1983 }
1984 default:
1985 LOG(ERROR) << __func__ << ", invalid state transition, from: "
1986 << static_cast<int>(ase->state) << ", to: "
1987 << static_cast<int>(
1988 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
1989 StopStream(group);
1990 break;
1991 }
1992 }
1993
PrepareAndSendEnable(LeAudioDevice * leAudioDevice)1994 void PrepareAndSendEnable(LeAudioDevice* leAudioDevice) {
1995 struct le_audio::client_parser::ascs::ctp_enable conf;
1996 std::vector<struct le_audio::client_parser::ascs::ctp_enable> confs;
1997 std::vector<uint8_t> value;
1998 struct ase* ase;
1999
2000 ase = leAudioDevice->GetFirstActiveAse();
2001 LOG_ASSERT(ase) << __func__ << " shouldn't be called without an active ASE";
2002 do {
2003 LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
2004 leAudioDevice->address_.ToString().c_str(), ase->id,
2005 ase->cis_id, ToString(ase->state).c_str());
2006 conf.ase_id = ase->id;
2007 conf.metadata = ase->metadata;
2008 confs.push_back(conf);
2009 } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
2010
2011 le_audio::client_parser::ascs::PrepareAseCtpEnable(confs, value);
2012
2013 BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_,
2014 leAudioDevice->ctp_hdls_.val_hdl, value,
2015 GATT_WRITE_NO_RSP, NULL, NULL);
2016 }
2017
PrepareAndSendDisable(LeAudioDevice * leAudioDevice)2018 void PrepareAndSendDisable(LeAudioDevice* leAudioDevice) {
2019 ase* ase = leAudioDevice->GetFirstActiveAse();
2020 LOG_ASSERT(ase) << __func__ << " shouldn't be called without an active ASE";
2021
2022 std::vector<uint8_t> ids;
2023 do {
2024 LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
2025 leAudioDevice->address_.ToString().c_str(), ase->id,
2026 ase->cis_id, ToString(ase->state).c_str());
2027 ids.push_back(ase->id);
2028 } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
2029
2030 std::vector<uint8_t> value;
2031 le_audio::client_parser::ascs::PrepareAseCtpDisable(ids, value);
2032
2033 BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_,
2034 leAudioDevice->ctp_hdls_.val_hdl, value,
2035 GATT_WRITE_NO_RSP, NULL, NULL);
2036 }
2037
PrepareAndSendRelease(LeAudioDevice * leAudioDevice)2038 void PrepareAndSendRelease(LeAudioDevice* leAudioDevice) {
2039 ase* ase = leAudioDevice->GetFirstActiveAse();
2040 LOG_ASSERT(ase) << __func__ << " shouldn't be called without an active ASE";
2041
2042 std::vector<uint8_t> ids;
2043 do {
2044 LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
2045 leAudioDevice->address_.ToString().c_str(), ase->id,
2046 ase->cis_id, ToString(ase->state).c_str());
2047 ids.push_back(ase->id);
2048 } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
2049
2050 std::vector<uint8_t> value;
2051 le_audio::client_parser::ascs::PrepareAseCtpRelease(ids, value);
2052
2053 BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_,
2054 leAudioDevice->ctp_hdls_.val_hdl, value,
2055 GATT_WRITE_NO_RSP, NULL, NULL);
2056 }
2057
PrepareAndSendConfigQos(LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)2058 void PrepareAndSendConfigQos(LeAudioDeviceGroup* group,
2059 LeAudioDevice* leAudioDevice) {
2060 std::vector<struct le_audio::client_parser::ascs::ctp_qos_conf> confs;
2061
2062 bool validate_transport_latency = false;
2063 bool validate_max_sdu_size = false;
2064
2065 for (struct ase* ase = leAudioDevice->GetFirstActiveAse(); ase != nullptr;
2066 ase = leAudioDevice->GetNextActiveAse(ase)) {
2067 LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
2068 leAudioDevice->address_.ToString().c_str(), ase->id,
2069 ase->cis_id, ToString(ase->state).c_str());
2070
2071 /* TODO: Configure first ASE qos according to context type */
2072 struct le_audio::client_parser::ascs::ctp_qos_conf conf;
2073 conf.ase_id = ase->id;
2074 conf.cig = group->group_id_;
2075 conf.cis = ase->cis_id;
2076 conf.framing = group->GetFraming();
2077 conf.phy = group->GetPhyBitmask(ase->direction);
2078 conf.max_sdu = ase->max_sdu_size;
2079 conf.retrans_nb = ase->retrans_nb;
2080 if (!group->GetPresentationDelay(&conf.pres_delay, ase->direction)) {
2081 LOG_ERROR("inconsistent presentation delay for group");
2082 group->PrintDebugState();
2083 StopStream(group);
2084 return;
2085 }
2086
2087 conf.sdu_interval = group->GetSduInterval(ase->direction);
2088 if (!conf.sdu_interval) {
2089 LOG_ERROR("unsupported SDU interval for group");
2090 group->PrintDebugState();
2091 StopStream(group);
2092 return;
2093 }
2094
2095 if (ase->direction == le_audio::types::kLeAudioDirectionSink) {
2096 conf.max_transport_latency = group->GetMaxTransportLatencyMtos();
2097 } else {
2098 conf.max_transport_latency = group->GetMaxTransportLatencyStom();
2099 }
2100
2101 if (conf.max_transport_latency >
2102 le_audio::types::kMaxTransportLatencyMin) {
2103 validate_transport_latency = true;
2104 }
2105
2106 if (conf.max_sdu > 0) {
2107 validate_max_sdu_size = true;
2108 }
2109 confs.push_back(conf);
2110 }
2111
2112 if (confs.size() == 0 || !validate_transport_latency ||
2113 !validate_max_sdu_size) {
2114 LOG_ERROR("Invalid configuration or latency or sdu size");
2115 group->PrintDebugState();
2116 StopStream(group);
2117 return;
2118 }
2119
2120 std::vector<uint8_t> value;
2121 le_audio::client_parser::ascs::PrepareAseCtpConfigQos(confs, value);
2122 BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_,
2123 leAudioDevice->ctp_hdls_.val_hdl, value,
2124 GATT_WRITE_NO_RSP, NULL, NULL);
2125 }
2126
PrepareAndSendUpdateMetadata(LeAudioDevice * leAudioDevice,le_audio::types::AudioContexts context_type,const std::vector<uint8_t> & ccid_list)2127 void PrepareAndSendUpdateMetadata(LeAudioDevice* leAudioDevice,
2128 le_audio::types::AudioContexts context_type,
2129 const std::vector<uint8_t>& ccid_list) {
2130 std::vector<struct le_audio::client_parser::ascs::ctp_update_metadata>
2131 confs;
2132
2133 if (!leAudioDevice->IsMetadataChanged(context_type, ccid_list)) return;
2134
2135 /* Request server to update ASEs with new metadata */
2136 for (struct ase* ase = leAudioDevice->GetFirstActiveAse(); ase != nullptr;
2137 ase = leAudioDevice->GetNextActiveAse(ase)) {
2138 LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
2139 leAudioDevice->address_.ToString().c_str(), ase->id,
2140 ase->cis_id, ToString(ase->state).c_str());
2141
2142 if (ase->state != AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING &&
2143 ase->state != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
2144 /* This might happen when update metadata happens on late connect */
2145 LOG_DEBUG(
2146 "Metadata for ase_id %d cannot be updated due to invalid ase state "
2147 "- see log above",
2148 ase->id);
2149 continue;
2150 }
2151
2152 /* Filter multidirectional audio context for each ase direction */
2153 auto directional_audio_context =
2154 context_type & leAudioDevice->GetAvailableContexts(ase->direction);
2155 if (directional_audio_context.any()) {
2156 ase->metadata =
2157 leAudioDevice->GetMetadata(directional_audio_context, ccid_list);
2158 } else {
2159 ase->metadata = leAudioDevice->GetMetadata(
2160 AudioContexts(LeAudioContextType::UNSPECIFIED),
2161 std::vector<uint8_t>());
2162 }
2163
2164 struct le_audio::client_parser::ascs::ctp_update_metadata conf;
2165
2166 conf.ase_id = ase->id;
2167 conf.metadata = ase->metadata;
2168
2169 confs.push_back(conf);
2170 }
2171
2172 if (confs.size() != 0) {
2173 std::vector<uint8_t> value;
2174 le_audio::client_parser::ascs::PrepareAseCtpUpdateMetadata(confs, value);
2175
2176 BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_,
2177 leAudioDevice->ctp_hdls_.val_hdl, value,
2178 GATT_WRITE_NO_RSP, NULL, NULL);
2179 }
2180 }
2181
PrepareAndSendReceiverStartReady(LeAudioDevice * leAudioDevice,struct ase * ase)2182 void PrepareAndSendReceiverStartReady(LeAudioDevice* leAudioDevice,
2183 struct ase* ase) {
2184 std::vector<uint8_t> ids;
2185 std::vector<uint8_t> value;
2186
2187 do {
2188 if (ase->direction == le_audio::types::kLeAudioDirectionSource)
2189 ids.push_back(ase->id);
2190 } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
2191
2192 if (ids.size() > 0) {
2193 le_audio::client_parser::ascs::PrepareAseCtpAudioReceiverStartReady(
2194 ids, value);
2195
2196 BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_,
2197 leAudioDevice->ctp_hdls_.val_hdl, value,
2198 GATT_WRITE_NO_RSP, NULL, NULL);
2199
2200 return;
2201 }
2202 }
2203
AseStateMachineProcessEnabling(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)2204 void AseStateMachineProcessEnabling(
2205 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
2206 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
2207 if (!group) {
2208 LOG(ERROR) << __func__ << ", leAudioDevice doesn't belong to any group";
2209 return;
2210 }
2211
2212 switch (ase->state) {
2213 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
2214 ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING;
2215
2216 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
2217 if (ase->data_path_state < AudioStreamDataPathState::CIS_PENDING) {
2218 /* We are here because of the reconnection of the single device. */
2219 CisCreateForDevice(leAudioDevice);
2220 }
2221
2222 if (!leAudioDevice->HaveAllActiveAsesCisEst()) {
2223 /* More cis established events has to come */
2224 return;
2225 }
2226
2227 if (!leAudioDevice->IsReadyToCreateStream()) {
2228 /* Device still remains in ready to create stream state. It means
2229 * that more enabling status notifications has to come.
2230 */
2231 return;
2232 }
2233
2234 /* All CISes created. Send start ready for source ASE before we can go
2235 * to streaming state.
2236 */
2237 struct ase* ase = leAudioDevice->GetFirstActiveAse();
2238 ASSERT_LOG(ase != nullptr,
2239 "shouldn't be called without an active ASE, device %s",
2240 leAudioDevice->address_.ToString().c_str());
2241 PrepareAndSendReceiverStartReady(leAudioDevice, ase);
2242
2243 return;
2244 }
2245
2246 if (leAudioDevice->IsReadyToCreateStream())
2247 ProcessGroupEnable(group, leAudioDevice);
2248
2249 break;
2250
2251 case AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING:
2252 /* Enable/Switch Content */
2253 break;
2254 default:
2255 LOG(ERROR) << __func__ << ", invalid state transition, from: "
2256 << static_cast<int>(ase->state) << ", to: "
2257 << static_cast<int>(
2258 AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING);
2259 StopStream(group);
2260 break;
2261 }
2262 }
2263
AseStateMachineProcessStreaming(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,uint8_t * data,uint16_t len,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)2264 void AseStateMachineProcessStreaming(
2265 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
2266 uint8_t* data, uint16_t len, LeAudioDeviceGroup* group,
2267 LeAudioDevice* leAudioDevice) {
2268 if (!group) {
2269 LOG(ERROR) << __func__ << ", leAudioDevice doesn't belong to any group";
2270
2271 return;
2272 }
2273
2274 switch (ase->state) {
2275 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
2276 /* As per ASCS 1.0 :
2277 * If a CIS has been established and the server is acting as Audio Sink
2278 * for the ASE, and if the server is ready to receive audio data
2279 * transmitted by the client, the server may autonomously initiate the
2280 * Receiver Start Ready, as defined in Section 5.4, without first
2281 * sending a notification of the ASE characteristic value in the
2282 * Enabling state.
2283 */
2284 if (ase->direction != le_audio::types::kLeAudioDirectionSink) {
2285 LOG(ERROR) << __func__ << ", invalid state transition, from: "
2286 << static_cast<int>(ase->state) << ", to: "
2287 << static_cast<int>(
2288 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
2289 StopStream(group);
2290 return;
2291 }
2292
2293 ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING;
2294
2295 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
2296 /* We are here because of the reconnection of the single device. */
2297 PrepareDataPath(group);
2298 return;
2299 }
2300
2301 if (leAudioDevice->IsReadyToCreateStream())
2302 ProcessGroupEnable(group, leAudioDevice);
2303
2304 break;
2305
2306 case AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING: {
2307 std::vector<uint8_t> value;
2308
2309 ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING;
2310
2311 if (!group->HaveAllActiveDevicesAsesTheSameState(
2312 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)) {
2313 /* More ASEs notification form this device has to come for this group
2314 */
2315
2316 return;
2317 }
2318
2319 if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
2320 /* We are here because of the reconnection of the single device. */
2321 PrepareDataPath(group);
2322 return;
2323 }
2324
2325 /* Last node is in streaming state */
2326 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
2327
2328 /* Not all CISes establish evens came */
2329 if (!group->IsGroupStreamReady()) return;
2330
2331 if (group->GetTargetState() ==
2332 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
2333 /* No more transition for group */
2334 alarm_cancel(watchdog_);
2335 PrepareDataPath(group);
2336
2337 return;
2338 } else {
2339 LOG_ERROR(", invalid state transition, from: %s, to: %s",
2340 ToString(group->GetState()).c_str(),
2341 ToString(group->GetTargetState()).c_str());
2342 StopStream(group);
2343 return;
2344 }
2345
2346 break;
2347 }
2348 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING: {
2349 struct le_audio::client_parser::ascs::ase_transient_state_params rsp;
2350
2351 if (!ParseAseStatusTransientStateParams(rsp, len, data)) {
2352 StopStream(group);
2353 return;
2354 }
2355
2356 /* Cache current set up metadata values for for further possible
2357 * reconfiguration
2358 */
2359 if (!rsp.metadata.empty()) {
2360 ase->metadata = rsp.metadata;
2361 }
2362
2363 break;
2364 }
2365 default:
2366 LOG(ERROR) << __func__ << ", invalid state transition, from: "
2367 << static_cast<int>(ase->state) << ", to: "
2368 << static_cast<int>(
2369 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
2370 StopStream(group);
2371 break;
2372 }
2373 }
2374
AseStateMachineProcessDisabling(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)2375 void AseStateMachineProcessDisabling(
2376 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
2377 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
2378 if (!group) {
2379 LOG(ERROR) << __func__ << ", leAudioDevice doesn't belong to any group";
2380
2381 return;
2382 }
2383
2384 if (ase->direction == le_audio::types::kLeAudioDirectionSink) {
2385 /* Sink ASE state machine does not have Disabling state */
2386 LOG_ERROR(", invalid state transition, from: %s , to: %s ",
2387 ToString(group->GetState()).c_str(),
2388 ToString(group->GetTargetState()).c_str());
2389 StopStream(group);
2390 return;
2391 }
2392
2393 switch (ase->state) {
2394 case AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING:
2395 /* TODO: Disable */
2396 break;
2397 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
2398 ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING;
2399
2400 /* Process the Disable Transition of the rest of group members if no
2401 * more ASE notifications has to come from this device. */
2402 if (leAudioDevice->IsReadyToSuspendStream())
2403 ProcessGroupDisable(group, leAudioDevice);
2404
2405 break;
2406
2407 default:
2408 LOG(ERROR) << __func__ << ", invalid state transition, from: "
2409 << static_cast<int>(ase->state) << ", to: "
2410 << static_cast<int>(
2411 AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING);
2412 StopStream(group);
2413 break;
2414 }
2415 }
2416
AseStateMachineProcessReleasing(struct le_audio::client_parser::ascs::ase_rsp_hdr & arh,struct ase * ase,LeAudioDeviceGroup * group,LeAudioDevice * leAudioDevice)2417 void AseStateMachineProcessReleasing(
2418 struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
2419 LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
2420 if (!group) {
2421 LOG(ERROR) << __func__ << ", leAudioDevice doesn't belong to any group";
2422
2423 return;
2424 }
2425
2426 switch (ase->state) {
2427 case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
2428 case AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING: {
2429 ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING;
2430 break;
2431 }
2432 case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
2433 /* At this point all of the active ASEs within group are released. */
2434 RemoveCigForGroup(group);
2435
2436 ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING;
2437 if (group->HaveAllActiveDevicesAsesTheSameState(
2438 AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING))
2439 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING);
2440
2441 break;
2442
2443 case AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING:
2444 case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING: {
2445 ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING;
2446
2447 /* Happens when bi-directional completive ASE releasing state came */
2448 if (ase->data_path_state == AudioStreamDataPathState::CIS_DISCONNECTING)
2449 break;
2450
2451 if (ase->data_path_state ==
2452 AudioStreamDataPathState::DATA_PATH_ESTABLISHED) {
2453 RemoveDataPathByCisHandle(leAudioDevice, ase->cis_conn_hdl);
2454 } else if (ase->data_path_state ==
2455 AudioStreamDataPathState::CIS_ESTABLISHED ||
2456 ase->data_path_state ==
2457 AudioStreamDataPathState::CIS_PENDING) {
2458 RemoveCisFromStreamConfiguration(group, leAudioDevice,
2459 ase->cis_conn_hdl);
2460 IsoManager::GetInstance()->DisconnectCis(ase->cis_conn_hdl,
2461 HCI_ERR_PEER_USER);
2462 } else {
2463 DLOG(INFO) << __func__ << ", Nothing to do ase data path state: "
2464 << static_cast<int>(ase->data_path_state);
2465 }
2466 break;
2467 }
2468 default:
2469 LOG(ERROR) << __func__ << ", invalid state transition, from: "
2470 << static_cast<int>(ase->state) << ", to: "
2471 << static_cast<int>(
2472 AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING);
2473 break;
2474 }
2475 }
2476
ProcessGroupEnable(LeAudioDeviceGroup * group,LeAudioDevice * device)2477 void ProcessGroupEnable(LeAudioDeviceGroup* group, LeAudioDevice* device) {
2478 /* Enable ASEs for next device in group. */
2479 LeAudioDevice* deviceNext = group->GetNextActiveDevice(device);
2480 if (deviceNext) {
2481 PrepareAndSendEnable(deviceNext);
2482 return;
2483 }
2484
2485 /* At this point all of the active ASEs within group are enabled. The server
2486 * might perform autonomous state transition for Sink ASE and skip Enabling
2487 * state notification and transit to Streaming directly. So check the group
2488 * state, because we might be ready to create CIS. */
2489 if (group->HaveAllActiveDevicesAsesTheSameState(
2490 AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)) {
2491 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
2492 } else {
2493 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING);
2494 }
2495
2496 if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
2497 CisCreate(group);
2498 } else {
2499 LOG_ERROR(", invalid state transition, from: %s , to: %s ",
2500 ToString(group->GetState()).c_str(),
2501 ToString(group->GetTargetState()).c_str());
2502 StopStream(group);
2503 }
2504 }
2505
ProcessGroupDisable(LeAudioDeviceGroup * group,LeAudioDevice * device)2506 void ProcessGroupDisable(LeAudioDeviceGroup* group, LeAudioDevice* device) {
2507 /* Disable ASEs for next device in group. */
2508 LeAudioDevice* deviceNext = group->GetNextActiveDevice(device);
2509 if (deviceNext) {
2510 PrepareAndSendDisable(deviceNext);
2511 return;
2512 }
2513
2514 /* At this point all of the active ASEs within group are disabled. As there
2515 * is no Disabling state for Sink ASE, it might happen that all of the
2516 * active ASEs are Sink ASE and will transit to QoS state. So check
2517 * the group state, because we might be ready to release data path. */
2518 if (group->HaveAllActiveDevicesAsesTheSameState(
2519 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED)) {
2520 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
2521 } else {
2522 group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING);
2523 }
2524
2525 /* Transition to QoS configured is done by CIS disconnection */
2526 if (group->GetTargetState() ==
2527 AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) {
2528 ReleaseDataPath(group);
2529 } else {
2530 LOG_ERROR(", invalid state transition, from: %s , to: %s ",
2531 ToString(group->GetState()).c_str(),
2532 ToString(group->GetTargetState()).c_str());
2533 StopStream(group);
2534 }
2535 }
2536 };
2537 } // namespace
2538
2539 namespace le_audio {
Initialize(Callbacks * state_machine_callbacks_)2540 void LeAudioGroupStateMachine::Initialize(Callbacks* state_machine_callbacks_) {
2541 if (instance) {
2542 LOG(ERROR) << "Already initialized";
2543 return;
2544 }
2545
2546 instance = new LeAudioGroupStateMachineImpl(state_machine_callbacks_);
2547 }
2548
Cleanup()2549 void LeAudioGroupStateMachine::Cleanup() {
2550 if (!instance) return;
2551
2552 LeAudioGroupStateMachineImpl* ptr = instance;
2553 instance = nullptr;
2554
2555 delete ptr;
2556 }
2557
Get()2558 LeAudioGroupStateMachine* LeAudioGroupStateMachine::Get() {
2559 CHECK(instance);
2560 return instance;
2561 }
2562 } // namespace le_audio
2563