1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/hci/sequential_command_runner.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
18 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
19 #include "pw_bluetooth_sapphire/internal/host/testing/controller_test.h"
20 #include "pw_bluetooth_sapphire/internal/host/testing/mock_controller.h"
21 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
22 #include "pw_bluetooth_sapphire/internal/host/testing/test_packets.h"
23
24 namespace bt::hci {
25 namespace {
26
27 constexpr hci_spec::OpCode kTestOpCode = 0xFFFF;
28 constexpr hci_spec::OpCode kTestOpCode2 = 0xF00F;
29
30 using bt::testing::CommandTransaction;
31
32 using TestingBase =
33 bt::testing::FakeDispatcherControllerTest<bt::testing::MockController>;
34
35 class SequentialCommandRunnerTest : public TestingBase {
36 public:
37 SequentialCommandRunnerTest() = default;
38 ~SequentialCommandRunnerTest() override = default;
39 };
40
41 using HCI_SequentialCommandRunnerTest = SequentialCommandRunnerTest;
42
TEST_F(SequentialCommandRunnerTest,SequentialCommandRunner)43 TEST_F(SequentialCommandRunnerTest, SequentialCommandRunner) {
44 // HCI command with custom opcode FFFF.
45 StaticByteBuffer command_bytes(0xFF, 0xFF, 0x00);
46
47 StaticByteBuffer command_status_error_bytes(
48 hci_spec::kCommandStatusEventCode,
49 0x04, // parameter_total_size (4 byte payload)
50 pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE,
51 1,
52 0xFF,
53 0xFF);
54
55 StaticByteBuffer command_cmpl_error_bytes(
56 hci_spec::kCommandCompleteEventCode,
57 0x04, // parameter_total_size (4 byte payload)
58 1,
59 0xFF,
60 0xFF,
61 pw::bluetooth::emboss::StatusCode::RESERVED_0);
62
63 auto command_cmpl_success_bytes =
64 StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
65 0x04, // parameter_total_size (4 byte payload)
66 1,
67 0xFF,
68 0xFF,
69 pw::bluetooth::emboss::StatusCode::SUCCESS);
70
71 // Here we perform multiple test sequences where we queue up several commands
72 // in each sequence. We expect each sequence to terminate differently after
73 // the following HCI transactions:
74 //
75 // Sequence 1 (HCI packets)
76 // -> Command; <- error status
77 EXPECT_CMD_PACKET_OUT(
78 test_device(), command_bytes, &command_status_error_bytes);
79
80 // Sequence 2 (HCI packets)
81 // -> Command; <- error complete
82 EXPECT_CMD_PACKET_OUT(
83 test_device(), command_bytes, &command_cmpl_error_bytes);
84
85 // Sequence 3 (HCI packets)
86 // -> Command; <- success complete
87 // -> Command; <- error complete
88 EXPECT_CMD_PACKET_OUT(
89 test_device(), command_bytes, &command_cmpl_success_bytes);
90 EXPECT_CMD_PACKET_OUT(
91 test_device(), command_bytes, &command_cmpl_error_bytes);
92
93 // Sequence 4 (HCI packets)
94 // -> Command; <- success complete
95 // -> Command; <- success complete
96 EXPECT_CMD_PACKET_OUT(
97 test_device(), command_bytes, &command_cmpl_success_bytes);
98 EXPECT_CMD_PACKET_OUT(
99 test_device(), command_bytes, &command_cmpl_success_bytes);
100
101 // Sequence 5 (HCI packets)
102 // -> Command; <- success complete
103 // -> Command; <- success complete
104 EXPECT_CMD_PACKET_OUT(
105 test_device(), command_bytes, &command_cmpl_success_bytes);
106 EXPECT_CMD_PACKET_OUT(
107 test_device(), command_bytes, &command_cmpl_success_bytes);
108
109 Result<> status = fit::ok();
110 int status_cb_called = 0;
111 auto status_cb = [&](Result<> cb_status) {
112 status = cb_status;
113 status_cb_called++;
114 };
115
116 int cb_called = 0;
117 auto cb = [&](const EventPacket& event) { cb_called++; };
118
119 // Sequence 1 (test)
120 SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
121 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
122
123 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
124 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
125 cb); // <-- Should not run
126
127 EXPECT_TRUE(cmd_runner.IsReady());
128 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
129
130 cmd_runner.RunCommands(status_cb);
131 EXPECT_FALSE(cmd_runner.IsReady());
132 RunUntilIdle();
133 EXPECT_TRUE(cmd_runner.IsReady());
134 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
135 EXPECT_EQ(1, cb_called);
136 EXPECT_EQ(1, status_cb_called);
137 EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE),
138 status);
139 cb_called = 0;
140
141 // Sequence 2 (test)
142 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
143 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
144 cb); // <-- Should not run
145
146 EXPECT_TRUE(cmd_runner.IsReady());
147 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
148
149 cmd_runner.RunCommands(status_cb);
150 EXPECT_FALSE(cmd_runner.IsReady());
151 RunUntilIdle();
152 EXPECT_TRUE(cmd_runner.IsReady());
153 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
154 EXPECT_EQ(1, cb_called);
155 EXPECT_EQ(2, status_cb_called);
156 EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::RESERVED_0), status);
157 cb_called = 0;
158
159 // Sequence 3 (test)
160 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
161 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
162 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
163 cb); // <-- Should not run
164
165 EXPECT_TRUE(cmd_runner.IsReady());
166 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
167
168 cmd_runner.RunCommands(status_cb);
169 EXPECT_FALSE(cmd_runner.IsReady());
170 RunUntilIdle();
171 EXPECT_TRUE(cmd_runner.IsReady());
172 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
173 EXPECT_EQ(2, cb_called);
174 EXPECT_EQ(3, status_cb_called);
175 EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::RESERVED_0), status);
176 cb_called = 0;
177
178 // Sequence 4 (test)
179 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
180 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
181
182 EXPECT_TRUE(cmd_runner.IsReady());
183 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
184
185 cmd_runner.RunCommands(status_cb);
186 EXPECT_FALSE(cmd_runner.IsReady());
187 RunUntilIdle();
188 EXPECT_TRUE(cmd_runner.IsReady());
189 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
190 EXPECT_EQ(2, cb_called);
191 EXPECT_EQ(4, status_cb_called);
192 EXPECT_EQ(fit::ok(), status);
193 cb_called = 0;
194 status_cb_called = 0;
195
196 // Sequence 5 (test) (no callback passed to QueueCommand)
197 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode));
198 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode));
199
200 EXPECT_TRUE(cmd_runner.IsReady());
201 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
202
203 cmd_runner.RunCommands(status_cb);
204 EXPECT_FALSE(cmd_runner.IsReady());
205 RunUntilIdle();
206 EXPECT_TRUE(cmd_runner.IsReady());
207 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
208 EXPECT_EQ(0, cb_called);
209 EXPECT_EQ(1, status_cb_called);
210 EXPECT_EQ(fit::ok(), status);
211 }
212
TEST_F(SequentialCommandRunnerTest,SequentialCommandRunnerCancel)213 TEST_F(SequentialCommandRunnerTest, SequentialCommandRunnerCancel) {
214 StaticByteBuffer command_bytes(0xFF, 0xFF, 0x00);
215
216 auto command_cmpl_error_bytes =
217 StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
218 0x04, // parameter_total_size (4 byte payload)
219 1,
220 0xFF,
221 0xFF,
222 pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE);
223
224 auto command_cmpl_success_bytes =
225 StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
226 0x04, // parameter_total_size (4 byte payload)
227 1,
228 0xFF,
229 0xFF,
230 pw::bluetooth::emboss::StatusCode::SUCCESS);
231
232 // Sequence 1
233 // -> Command; <- success complete
234 EXPECT_CMD_PACKET_OUT(
235 test_device(), command_bytes, &command_cmpl_success_bytes);
236
237 // Sequence 2
238 // -> Command; <- success complete
239 EXPECT_CMD_PACKET_OUT(
240 test_device(), command_bytes, &command_cmpl_success_bytes);
241
242 // Sequence 3
243 // -> Command; <- success complete
244 // -> Command; <- error complete
245 EXPECT_CMD_PACKET_OUT(
246 test_device(), command_bytes, &command_cmpl_success_bytes);
247 EXPECT_CMD_PACKET_OUT(
248 test_device(), command_bytes, &command_cmpl_error_bytes);
249
250 Result<> status = fit::ok();
251 int status_cb_called = 0;
252 auto status_cb = [&](Result<> cb_status) {
253 status = cb_status;
254 status_cb_called++;
255 };
256
257 int cb_called = 0;
258 auto cb = [&](const EventPacket& event) { cb_called++; };
259
260 // Sequence 1: Sequence will be cancelled after the first command.
261 SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
262 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
263 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
264 EXPECT_TRUE(cmd_runner.IsReady());
265 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
266
267 // Call RunCommands() and cancel the sequence immediately. The
268 // first command will go out but no successive packets should be sent.
269 // The status callback should be invoked
270 // No command callbacks should be called.
271 cmd_runner.RunCommands(status_cb);
272 EXPECT_FALSE(cmd_runner.IsReady());
273 cmd_runner.Cancel();
274
275 RunUntilIdle();
276 EXPECT_TRUE(cmd_runner.IsReady());
277 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
278 EXPECT_EQ(0, cb_called);
279 EXPECT_EQ(1, status_cb_called);
280 EXPECT_EQ(ToResult(HostError::kCanceled), status);
281 cb_called = 0;
282 status_cb_called = 0;
283 status = fit::ok();
284
285 // Sequence 2: Sequence will be cancelled after first command. This tests
286 // canceling a sequence from a CommandCompleteCallback.
287 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
288 [&](const EventPacket& event) {
289 bt_log(TRACE, "hci-test", "callback called");
290 cmd_runner.Cancel();
291 EXPECT_TRUE(cmd_runner.IsReady());
292 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
293 });
294 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
295 cb); // <-- Should not run
296 EXPECT_TRUE(cmd_runner.IsReady());
297 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
298
299 cmd_runner.RunCommands(status_cb);
300 EXPECT_FALSE(cmd_runner.IsReady());
301
302 // |status_cb| is expected to get called with kCanceled
303 RunUntilIdle();
304 EXPECT_TRUE(cmd_runner.IsReady());
305 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
306
307 EXPECT_EQ(0, cb_called);
308 EXPECT_EQ(1, status_cb_called);
309 EXPECT_EQ(ToResult(HostError::kCanceled), status);
310 cb_called = 0;
311 status_cb_called = 0;
312 status = fit::ok();
313
314 // Sequence 3: Sequence will be cancelled after first command and immediately
315 // followed by a second command which will fail. This tests canceling a
316 // sequence and initiating a new one from a CommandCompleteCallback.
317 cmd_runner.QueueCommand(
318 CommandPacket::New(kTestOpCode), [&](const EventPacket& event) {
319 cmd_runner.Cancel();
320 EXPECT_TRUE(cmd_runner.IsReady());
321 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
322
323 EXPECT_EQ(1, status_cb_called);
324 EXPECT_EQ(ToResult(HostError::kCanceled), status);
325
326 // Queue multiple commands (only one will execute since MockController
327 // will send back an error status).
328 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
329 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
330 cb); // <-- Should not run
331 cmd_runner.RunCommands(status_cb);
332 });
333 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
334 cb); // <-- Should not run
335 EXPECT_TRUE(cmd_runner.IsReady());
336 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
337
338 cmd_runner.RunCommands(status_cb);
339 EXPECT_FALSE(cmd_runner.IsReady());
340
341 RunUntilIdle();
342
343 EXPECT_TRUE(cmd_runner.IsReady());
344 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
345
346 // The cb queued from inside the first callback should have been called.
347 EXPECT_EQ(1, cb_called);
348 // The result callback should have been called with the failure result.
349 EXPECT_EQ(2, status_cb_called);
350 EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE),
351 status);
352 }
353
TEST_F(SequentialCommandRunnerTest,ParallelCommands)354 TEST_F(SequentialCommandRunnerTest, ParallelCommands) {
355 // Need to signal to the queue that we can run more than one command at once.
356 auto command_status_queue_increase =
357 StaticByteBuffer(hci_spec::kCommandStatusEventCode,
358 0x04, // parameter_total_size (4 byte payload)
359 pw::bluetooth::emboss::StatusCode::SUCCESS,
360 250,
361 0x00,
362 0x00);
363 // HCI command with custom opcode FFFF.
364 StaticByteBuffer command_bytes(0xFF, 0xFF, 0x00);
365 auto command_status_error_bytes =
366 StaticByteBuffer(hci_spec::kCommandStatusEventCode,
367 0x04, // parameter_total_size (4 byte payload)
368 pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE,
369 2,
370 0xFF,
371 0xFF);
372
373 auto command_cmpl_error_bytes =
374 StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
375 0x04, // parameter_total_size (4 byte payload)
376 2,
377 0xFF,
378 0xFF,
379 pw::bluetooth::emboss::StatusCode::RESERVED_0);
380
381 auto command_cmpl_success_bytes =
382 StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
383 0x04, // parameter_total_size (4 byte payload)
384 2,
385 0xFF,
386 0xFF,
387 pw::bluetooth::emboss::StatusCode::SUCCESS);
388
389 // HCI command with custom opcode F00F.
390 StaticByteBuffer command2_bytes(0x0F, 0xF0, 0x00);
391 auto command2_status_error_bytes =
392 StaticByteBuffer(hci_spec::kCommandStatusEventCode,
393 0x04, // parameter_total_size (4 byte payload)
394 pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE,
395 2,
396 0x0F,
397 0xF0);
398
399 auto command2_cmpl_error_bytes =
400 StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
401 0x04, // parameter_total_size (4 byte payload)
402 2,
403 0x0F,
404 0xF0,
405 pw::bluetooth::emboss::StatusCode::RESERVED_0);
406
407 auto command2_cmpl_success_bytes =
408 StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
409 0x04, // parameter_total_size (4 byte payload)
410 2,
411 0x0F,
412 0xF0,
413 pw::bluetooth::emboss::StatusCode::SUCCESS);
414
415 test_device()->SendCommandChannelPacket(command_status_queue_increase);
416
417 // Parallel commands should all run before commands that require success.
418 // command and command2 are answered in opposite order because they should be
419 // sent simultaneously.
420 EXPECT_CMD_PACKET_OUT(test_device(), command_bytes, );
421 EXPECT_CMD_PACKET_OUT(test_device(), command2_bytes, );
422 EXPECT_CMD_PACKET_OUT(
423 test_device(), command_bytes, &command_cmpl_success_bytes);
424 EXPECT_CMD_PACKET_OUT(
425 test_device(), command_bytes, &command_cmpl_success_bytes);
426 EXPECT_CMD_PACKET_OUT(
427 test_device(), command_bytes, &command_cmpl_success_bytes);
428
429 int cb_called = 0;
430 auto cb = [&](const hci::EventPacket&) { cb_called++; };
431
432 int status_cb_called = 0;
433 Result<> status = ToResult(HostError::kFailed);
434 auto status_cb = [&](Result<> cb_status) {
435 status = cb_status;
436 status_cb_called++;
437 };
438
439 SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
440
441 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb, /*wait=*/false);
442 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode2), cb, /*wait=*/false);
443 cmd_runner.QueueCommand(
444 CommandPacket::New(kTestOpCode),
445 [&](const hci::EventPacket&) {
446 EXPECT_EQ(2, cb_called);
447 cb_called++;
448 },
449 /*wait=*/true);
450 // We can also queue to the end of the queue without the last one being a
451 // wait.
452 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb, /*wait=*/false);
453 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb, /*wait=*/false);
454 cmd_runner.RunCommands(status_cb);
455 EXPECT_FALSE(cmd_runner.IsReady());
456
457 RunUntilIdle();
458 // The first two commands should have been sent but no responses are back yet.
459 EXPECT_EQ(0, cb_called);
460
461 // It should not matter if they get answered in opposite order.
462 test_device()->SendCommandChannelPacket(command2_cmpl_success_bytes);
463 test_device()->SendCommandChannelPacket(command_cmpl_success_bytes);
464 RunUntilIdle();
465
466 EXPECT_EQ(5, cb_called);
467 EXPECT_EQ(fit::ok(), status);
468 EXPECT_EQ(1, status_cb_called);
469 cb_called = 0;
470 status_cb_called = 0;
471
472 // If any simultaneous commands fail, the sequence fails and the command
473 // sequence is terminated.
474 EXPECT_CMD_PACKET_OUT(test_device(), command_bytes, );
475 EXPECT_CMD_PACKET_OUT(test_device(), command2_bytes, );
476
477 int cb_0_called = 0;
478 auto cb_0 = [&](const hci::EventPacket&) { cb_0_called++; };
479 int cb_1_called = 0;
480 auto cb_1 = [&](const hci::EventPacket&) { cb_1_called++; };
481 int cb_2_called = 0;
482 auto cb_2 = [&](const hci::EventPacket&) { cb_2_called++; };
483 cmd_runner.QueueCommand(
484 CommandPacket::New(kTestOpCode), cb_0, /*wait=*/false);
485 cmd_runner.QueueCommand(
486 CommandPacket::New(kTestOpCode2), cb_1, /*wait=*/false);
487 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
488 cb_2); // shouldn't run
489
490 cmd_runner.RunCommands(status_cb);
491 EXPECT_FALSE(cmd_runner.IsReady());
492
493 RunUntilIdle();
494 // The first two commands should have been sent but no responses are back yet.
495 EXPECT_EQ(0, cb_0_called);
496 EXPECT_EQ(0, cb_1_called);
497 EXPECT_EQ(0, cb_2_called);
498
499 test_device()->SendCommandChannelPacket(command_status_error_bytes);
500 test_device()->SendCommandChannelPacket(command2_cmpl_success_bytes);
501 RunUntilIdle();
502
503 // Only the first command's callback should be called, as further callbacks
504 // will be canceled due to the error status.
505 EXPECT_EQ(1, cb_0_called);
506 EXPECT_EQ(0, cb_1_called);
507 EXPECT_EQ(0, cb_2_called);
508 EXPECT_EQ(1, status_cb_called);
509 EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE),
510 status);
511 }
512
TEST_F(SequentialCommandRunnerTest,CommandCompletesOnStatusEvent)513 TEST_F(SequentialCommandRunnerTest, CommandCompletesOnStatusEvent) {
514 auto command = bt::testing::EmptyCommandPacket(kTestOpCode);
515 auto command0_status_event = bt::testing::CommandStatusPacket(
516 kTestOpCode, pw::bluetooth::emboss::StatusCode::SUCCESS);
517
518 auto command1 = bt::testing::EmptyCommandPacket(kTestOpCode2);
519 auto command1_cmpl_event = bt::testing::CommandCompletePacket(
520 kTestOpCode2, pw::bluetooth::emboss::StatusCode::SUCCESS);
521
522 Result<> status = fit::ok();
523 int status_cb_called = 0;
524 auto status_cb = [&](Result<> cb_status) {
525 status = cb_status;
526 status_cb_called++;
527 };
528
529 int cb_called = 0;
530 auto cb = [&](const EventPacket& event) { cb_called++; };
531
532 SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
533 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
534
535 EXPECT_CMD_PACKET_OUT(test_device(), command, &command0_status_event);
536 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
537 cb,
538 /*wait=*/false,
539 hci_spec::kCommandStatusEventCode);
540
541 EXPECT_CMD_PACKET_OUT(test_device(), command1, &command1_cmpl_event);
542 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode2), cb, /*wait=*/true);
543
544 EXPECT_TRUE(cmd_runner.IsReady());
545 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
546
547 cmd_runner.RunCommands(status_cb);
548 EXPECT_FALSE(cmd_runner.IsReady());
549 RunUntilIdle();
550 EXPECT_TRUE(cmd_runner.IsReady());
551 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
552 EXPECT_EQ(2, cb_called);
553 EXPECT_EQ(1, status_cb_called);
554 EXPECT_EQ(fit::ok(), status);
555 }
556
TEST_F(SequentialCommandRunnerTest,AsyncCommands)557 TEST_F(SequentialCommandRunnerTest, AsyncCommands) {
558 auto command = bt::testing::EmptyCommandPacket(hci_spec::kRemoteNameRequest);
559 auto command0_status_event = bt::testing::CommandStatusPacket(
560 hci_spec::kRemoteNameRequest, pw::bluetooth::emboss::StatusCode::SUCCESS);
561 auto command0_cmpl_event =
562 bt::testing::RemoteNameRequestCompletePacket(DeviceAddress());
563
564 auto command1 =
565 bt::testing::EmptyCommandPacket(hci_spec::kLEReadRemoteFeatures);
566 auto command1_status_event = bt::testing::CommandStatusPacket(
567 hci_spec::kLEReadRemoteFeatures,
568 pw::bluetooth::emboss::StatusCode::SUCCESS);
569 auto command1_cmpl_event = bt::testing::LEReadRemoteFeaturesCompletePacket(
570 /*conn=*/0x0000, hci_spec::LESupportedFeatures{0});
571
572 auto command2 =
573 bt::testing::EmptyCommandPacket(hci_spec::kReadRemoteVersionInfo);
574 auto command2_status_event = bt::testing::CommandStatusPacket(
575 hci_spec::kReadRemoteVersionInfo,
576 pw::bluetooth::emboss::StatusCode::SUCCESS);
577 auto command2_cmpl_event =
578 bt::testing::ReadRemoteVersionInfoCompletePacket(/*conn=*/0x0000);
579
580 Result<> status = fit::ok();
581 int status_cb_called = 0;
582 auto status_cb = [&](Result<> cb_status) {
583 status = cb_status;
584 status_cb_called++;
585 };
586
587 int cb_called = 0;
588 auto cb = [&](const EventPacket& event) { cb_called++; };
589
590 SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
591 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
592
593 EXPECT_CMD_PACKET_OUT(test_device(), command, &command0_status_event);
594 cmd_runner.QueueCommand(CommandPacket::New(hci_spec::kRemoteNameRequest),
595 cb,
596 /*wait=*/false,
597 hci_spec::kRemoteNameRequestCompleteEventCode);
598
599 EXPECT_CMD_PACKET_OUT(test_device(), command1, &command1_status_event);
600 cmd_runner.QueueLeAsyncCommand(
601 EmbossCommandPacket::New<pw::bluetooth::emboss::CommandHeaderView>(
602 hci_spec::kLEReadRemoteFeatures),
603 hci_spec::kLEReadRemoteFeaturesCompleteSubeventCode,
604 cb,
605 /*wait=*/false);
606
607 cmd_runner.QueueCommand(CommandPacket::New(hci_spec::kReadRemoteVersionInfo),
608 cb,
609 /*wait=*/true,
610 hci_spec::kReadRemoteVersionInfoCompleteEventCode);
611
612 EXPECT_TRUE(cmd_runner.IsReady());
613 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
614
615 cmd_runner.RunCommands(status_cb);
616 EXPECT_FALSE(cmd_runner.IsReady());
617
618 RunUntilIdle();
619 // Command 2 should wait on command 0 & command 1 complete events.
620 EXPECT_FALSE(cmd_runner.IsReady());
621 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
622 // Completing the commands out of order shouldn't matter.
623 test_device()->SendCommandChannelPacket(command1_cmpl_event);
624 test_device()->SendCommandChannelPacket(command0_cmpl_event);
625
626 EXPECT_CMD_PACKET_OUT(
627 test_device(), command2, &command2_status_event, &command2_cmpl_event);
628 RunUntilIdle();
629 EXPECT_TRUE(cmd_runner.IsReady());
630 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
631 EXPECT_EQ(3, cb_called);
632 EXPECT_EQ(1, status_cb_called);
633 EXPECT_EQ(fit::ok(), status);
634 }
635
TEST_F(SequentialCommandRunnerTest,ExclusiveAsyncCommands)636 TEST_F(SequentialCommandRunnerTest, ExclusiveAsyncCommands) {
637 auto command = bt::testing::EmptyCommandPacket(hci_spec::kRemoteNameRequest);
638 auto command0_status_event = bt::testing::CommandStatusPacket(
639 hci_spec::kRemoteNameRequest, pw::bluetooth::emboss::StatusCode::SUCCESS);
640 auto command0_cmpl_event =
641 bt::testing::RemoteNameRequestCompletePacket(DeviceAddress());
642
643 auto command1 =
644 bt::testing::EmptyCommandPacket(hci_spec::kReadRemoteVersionInfo);
645 auto command1_status_event = bt::testing::CommandStatusPacket(
646 hci_spec::kReadRemoteVersionInfo,
647 pw::bluetooth::emboss::StatusCode::SUCCESS);
648 auto command1_cmpl_event =
649 bt::testing::ReadRemoteVersionInfoCompletePacket(/*conn=*/0x0000);
650
651 Result<> status = fit::ok();
652 int status_cb_called = 0;
653 auto status_cb = [&](Result<> cb_status) {
654 status = cb_status;
655 status_cb_called++;
656 };
657
658 int cb_called = 0;
659 auto cb = [&](const EventPacket& event) { cb_called++; };
660
661 SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
662 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
663
664 EXPECT_CMD_PACKET_OUT(test_device(), command, &command0_status_event);
665 cmd_runner.QueueCommand(CommandPacket::New(hci_spec::kRemoteNameRequest),
666 cb,
667 /*wait=*/false,
668 hci_spec::kRemoteNameRequestCompleteEventCode);
669
670 // Even though command 1 is not waiting on command 0, it should remain queued
671 // due to the exclusion list.
672 cmd_runner.QueueCommand(CommandPacket::New(hci_spec::kReadRemoteVersionInfo),
673 cb,
674 /*wait=*/false,
675 hci_spec::kReadRemoteVersionInfoCompleteEventCode,
676 /*exclusions=*/{hci_spec::kRemoteNameRequest});
677
678 EXPECT_TRUE(cmd_runner.IsReady());
679 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
680
681 cmd_runner.RunCommands(status_cb);
682
683 RunUntilIdle();
684 EXPECT_FALSE(cmd_runner.IsReady());
685 // Command 1 is "sent" but queued in CommandChannel.
686 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
687 // Completing command 0 should send command 1.
688 test_device()->SendCommandChannelPacket(command0_cmpl_event);
689
690 EXPECT_CMD_PACKET_OUT(
691 test_device(), command1, &command1_status_event, &command1_cmpl_event);
692 RunUntilIdle();
693 EXPECT_TRUE(cmd_runner.IsReady());
694 EXPECT_EQ(2, cb_called);
695 EXPECT_EQ(1, status_cb_called);
696 EXPECT_EQ(fit::ok(), status);
697 }
698
TEST_F(SequentialCommandRunnerTest,CommandRunnerDestroyedBeforeSecondEventCallbackCalled)699 TEST_F(SequentialCommandRunnerTest,
700 CommandRunnerDestroyedBeforeSecondEventCallbackCalled) {
701 auto command = bt::testing::EmptyCommandPacket(hci_spec::kRemoteNameRequest);
702 auto command0_status_event = bt::testing::CommandStatusPacket(
703 hci_spec::kRemoteNameRequest, pw::bluetooth::emboss::StatusCode::SUCCESS);
704 auto command0_cmpl_event =
705 bt::testing::RemoteNameRequestCompletePacket(DeviceAddress());
706
707 auto command1 =
708 bt::testing::EmptyCommandPacket(hci_spec::kLEReadRemoteFeatures);
709 auto command1_status_event = bt::testing::CommandStatusPacket(
710 hci_spec::kLEReadRemoteFeatures,
711 pw::bluetooth::emboss::StatusCode::SUCCESS);
712 auto command1_cmpl_event = bt::testing::LEReadRemoteFeaturesCompletePacket(
713 /*conn=*/0x0000, hci_spec::LESupportedFeatures{0});
714
715 std::optional<SequentialCommandRunner> cmd_runner;
716 cmd_runner.emplace(cmd_channel()->AsWeakPtr());
717
718 Result<> status = fit::ok();
719 int status_cb_called = 0;
720 auto status_cb = [&](Result<> cb_status) {
721 status = cb_status;
722 status_cb_called++;
723 };
724
725 int cb_called = 0;
726 auto cb = [&](const EventPacket& event) {
727 if (cb_called == 0) {
728 cmd_runner.reset();
729 }
730 cb_called++;
731 };
732
733 EXPECT_FALSE(cmd_runner->HasQueuedCommands());
734
735 EXPECT_CMD_PACKET_OUT(
736 test_device(), command, &command0_status_event, &command0_cmpl_event);
737 cmd_runner->QueueCommand(CommandPacket::New(hci_spec::kRemoteNameRequest),
738 cb,
739 /*wait=*/false,
740 hci_spec::kRemoteNameRequestCompleteEventCode);
741
742 EXPECT_CMD_PACKET_OUT(test_device(), command1, &command1_status_event);
743 cmd_runner->QueueLeAsyncCommand(
744 CommandPacket::New(hci_spec::kLEReadRemoteFeatures),
745 hci_spec::kLEReadRemoteFeaturesCompleteSubeventCode,
746 cb,
747 /*wait=*/false);
748
749 EXPECT_TRUE(cmd_runner->IsReady());
750 EXPECT_TRUE(cmd_runner->HasQueuedCommands());
751
752 cmd_runner->RunCommands(status_cb);
753 EXPECT_FALSE(cmd_runner->IsReady());
754
755 RunUntilIdle();
756 EXPECT_FALSE(cmd_runner.has_value());
757 EXPECT_EQ(1, cb_called);
758 EXPECT_EQ(0, status_cb_called);
759 }
760
TEST_F(SequentialCommandRunnerTest,SequentialCommandRunnerDestroyedInCancelStatusCallbackDoesNotCrash)761 TEST_F(SequentialCommandRunnerTest,
762 SequentialCommandRunnerDestroyedInCancelStatusCallbackDoesNotCrash) {
763 std::optional<SequentialCommandRunner> cmd_runner;
764 cmd_runner.emplace(cmd_channel()->AsWeakPtr());
765
766 Result<> status = fit::ok();
767 int status_cb_called = 0;
768 auto status_cb = [&](Result<> cb_status) {
769 status = cb_status;
770 status_cb_called++;
771 cmd_runner.reset();
772 };
773
774 int cb_called = 0;
775 auto cb = [&](const EventPacket& event) { cb_called++; };
776
777 auto command = bt::testing::EmptyCommandPacket(kTestOpCode);
778 cmd_runner->QueueCommand(CommandPacket::New(kTestOpCode), cb);
779 EXPECT_CMD_PACKET_OUT(test_device(), command);
780 cmd_runner->RunCommands(status_cb);
781 EXPECT_FALSE(cmd_runner->IsReady());
782 cmd_runner->Cancel();
783
784 RunUntilIdle();
785 EXPECT_FALSE(cmd_runner);
786 EXPECT_EQ(0, cb_called);
787 EXPECT_EQ(1, status_cb_called);
788 EXPECT_EQ(ToResult(HostError::kCanceled), status);
789 }
790
TEST_F(SequentialCommandRunnerTest,QueueCommandsWhileAlreadyRunning)791 TEST_F(SequentialCommandRunnerTest, QueueCommandsWhileAlreadyRunning) {
792 auto command = bt::testing::EmptyCommandPacket(hci_spec::kRemoteNameRequest);
793 auto command0_status_event = bt::testing::CommandStatusPacket(
794 hci_spec::kRemoteNameRequest, pw::bluetooth::emboss::StatusCode::SUCCESS);
795 auto command0_cmpl_event =
796 bt::testing::RemoteNameRequestCompletePacket(DeviceAddress());
797
798 auto command1 =
799 bt::testing::EmptyCommandPacket(hci_spec::kLEReadRemoteFeatures);
800 auto command1_status_event = bt::testing::CommandStatusPacket(
801 hci_spec::kLEReadRemoteFeatures,
802 pw::bluetooth::emboss::StatusCode::SUCCESS);
803 auto command1_cmpl_event = bt::testing::LEReadRemoteFeaturesCompletePacket(
804 /*conn=*/0x0000, hci_spec::LESupportedFeatures{0});
805
806 auto command2 =
807 bt::testing::EmptyCommandPacket(hci_spec::kReadRemoteVersionInfo);
808 auto command2_status_event = bt::testing::CommandStatusPacket(
809 hci_spec::kReadRemoteVersionInfo,
810 pw::bluetooth::emboss::StatusCode::SUCCESS);
811 auto command2_cmpl_event =
812 bt::testing::ReadRemoteVersionInfoCompletePacket(/*conn=*/0x0000);
813
814 SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
815
816 Result<> status = fit::ok();
817 int status_cb_called = 0;
818 auto status_cb = [&](Result<> cb_status) {
819 status = cb_status;
820 status_cb_called++;
821 };
822
823 int cb_called = 0;
824 auto cb = [&](const EventPacket& event) { cb_called++; };
825
826 int name_cb_called = 0;
827 auto name_request_callback = [&](const EventPacket& event) {
828 name_cb_called++;
829
830 EXPECT_FALSE(cmd_runner.IsReady());
831 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
832
833 EXPECT_CMD_PACKET_OUT(
834 test_device(), command1, &command1_status_event, &command1_cmpl_event);
835 cmd_runner.QueueLeAsyncCommand(
836 CommandPacket::New(hci_spec::kLEReadRemoteFeatures),
837 hci_spec::kLEReadRemoteFeaturesCompleteSubeventCode,
838 cb,
839 /*wait=*/false);
840
841 EXPECT_CMD_PACKET_OUT(
842 test_device(), command2, &command2_status_event, &command2_cmpl_event);
843 cmd_runner.QueueCommand(
844 CommandPacket::New(hci_spec::kReadRemoteVersionInfo),
845 cb,
846 /*wait=*/false,
847 hci_spec::kReadRemoteVersionInfoCompleteEventCode);
848 };
849
850 EXPECT_CMD_PACKET_OUT(
851 test_device(), command, &command0_status_event, &command0_cmpl_event);
852 cmd_runner.QueueCommand(CommandPacket::New(hci_spec::kRemoteNameRequest),
853 name_request_callback,
854 /*wait=*/false,
855 hci_spec::kRemoteNameRequestCompleteEventCode);
856 EXPECT_TRUE(cmd_runner.IsReady());
857 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
858
859 cmd_runner.RunCommands(status_cb);
860 EXPECT_FALSE(cmd_runner.IsReady());
861
862 RunUntilIdle();
863 EXPECT_TRUE(cmd_runner.IsReady());
864 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
865 EXPECT_EQ(1, name_cb_called);
866 EXPECT_EQ(2, cb_called);
867 EXPECT_EQ(1, status_cb_called);
868 EXPECT_EQ(fit::ok(), status);
869 }
870
TEST_F(SequentialCommandRunnerTest,EmbossEventHandler)871 TEST_F(SequentialCommandRunnerTest, EmbossEventHandler) {
872 SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
873 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
874
875 // HCI command with custom opcode FFFF.
876 StaticByteBuffer command_bytes(0xFF, 0xFF, 0x00);
877 StaticByteBuffer command_cmpl_success_bytes(
878 hci_spec::kCommandCompleteEventCode,
879 0x04, // parameter_total_size (4 byte payload)
880 1,
881 0xFF,
882 0xFF,
883 pw::bluetooth::emboss::StatusCode::SUCCESS);
884
885 Result<> status = fit::ok();
886 int status_cb_called = 0;
887 auto status_cb = [&](Result<> cb_status) {
888 status = cb_status;
889 status_cb_called++;
890 };
891
892 int cb_called = 0;
893 SequentialCommandRunner::EmbossCommandCompleteCallback cb =
894 [&](const EmbossEventPacket& event) {
895 cb_called++;
896 EXPECT_THAT(event.data(), BufferEq(command_cmpl_success_bytes));
897 };
898
899 cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), std::move(cb));
900 EXPECT_TRUE(cmd_runner.IsReady());
901 EXPECT_TRUE(cmd_runner.HasQueuedCommands());
902
903 EXPECT_CMD_PACKET_OUT(
904 test_device(), command_bytes, &command_cmpl_success_bytes);
905 cmd_runner.RunCommands(status_cb);
906 EXPECT_FALSE(cmd_runner.IsReady());
907 RunUntilIdle();
908 EXPECT_TRUE(cmd_runner.IsReady());
909 EXPECT_FALSE(cmd_runner.HasQueuedCommands());
910 EXPECT_EQ(1, cb_called);
911 EXPECT_EQ(1, status_cb_called);
912 EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), status);
913 }
914
915 } // namespace
916 } // namespace bt::hci
917