RootCanal: Implement LL tests for LE periodic advertising

Added tests:
- LL/DDI/ADV/BV-26-C
- LL/DDI/SCN/BV-79-C

Fixed test:
- LL/DDI/ADV/BV-06-C
(broken after HCI disconnection code fixup patch)

Enabled test:
- LL/DDI/ADV/BV-20-C
(enabled by implementation of HCI LE Set Default Phy)

Bug: 275847929
Test: atest --host rootcanal_ll_test
Change-Id: Icde224c36b2b554dc3e28e521c716dc2eef617df
diff --git a/tools/rootcanal/py/controller.py b/tools/rootcanal/py/controller.py
index 99e93a4..cc0dbe2 100644
--- a/tools/rootcanal/py/controller.py
+++ b/tools/rootcanal/py/controller.py
@@ -38,6 +38,7 @@
         self.mask = le_features
         self.ll_privacy = (le_features & hci.LLFeaturesBits.LL_PRIVACY) != 0
         self.le_extended_advertising = (le_features & hci.LLFeaturesBits.LE_EXTENDED_ADVERTISING) != 0
+        self.le_periodic_advertising = (le_features & hci.LLFeaturesBits.LE_PERIODIC_ADVERTISING) != 0
 
 
 def generate_rpa(irk: bytes) -> hci.Address:
@@ -223,24 +224,33 @@
         assert evt.num_hci_command_packets == 1
         return evt
 
-    async def expect_ll(self, expected_pdu: typing.Union[ll.LinkLayerPacket, type], timeout: int = 3):
+    async def expect_ll(self,
+                        expected_pdus: typing.Union[list, typing.Union[ll.LinkLayerPacket, type]],
+                        timeout: int = 3) -> int:
+        if not isinstance(expected_pdus, list):
+            expected_pdus = [expected_pdus]
+
         packet = await asyncio.wait_for(self.controller.receive_ll(), timeout=timeout)
         pdu = ll.LinkLayerPacket.parse_all(packet)
 
-        if isinstance(expected_pdu, type) and not isinstance(pdu, expected_pdu):
-            print("received pdu of unexpected type")
-            print(f"expected pdu: {expected_pdu.__name__}")
-            print("received pdu:")
-            pdu.show()
-            self.assertTrue(False)
+        matched_index = -1
+        for (index, expected_pdu) in enumerate(expected_pdus):
+            if isinstance(expected_pdu, type) and isinstance(pdu, expected_pdu):
+                return index
+            if isinstance(expected_pdu, ll.LinkLayerPacket) and pdu == expected_pdu:
+                return index
 
-        if isinstance(expected_pdu, ll.LinkLayerPacket) and pdu != expected_pdu:
-            print("received unexpected pdu")
-            print("expected pdu:")
-            expected_pdu.show()
-            print("received pdu:")
-            pdu.show()
-            self.assertTrue(False)
+        print("received unexpected pdu:")
+        pdu.show()
+        print("expected pdus:")
+        for expected_pdu in expected_pdus:
+            if isinstance(expected_pdu, type):
+                print(f"- {expected_pdu.__name__}")
+            if isinstance(expected_pdu, ll.LinkLayerPacket):
+                print(f"- {expected_pdu.__class__.__name__}")
+                expected_pdu.show()
+
+        self.assertTrue(False)
 
     def tearDown(self):
         self.controller.stop()
diff --git a/tools/rootcanal/test/LL/DDI/ADV/BV_06_C.py b/tools/rootcanal/test/LL/DDI/ADV/BV_06_C.py
index 6c078af..38acd5b 100644
--- a/tools/rootcanal/test/LL/DDI/ADV/BV_06_C.py
+++ b/tools/rootcanal/test/LL/DDI/ADV/BV_06_C.py
@@ -104,7 +104,7 @@
         await self.expect_evt(
             hci.DisconnectionComplete(status=ErrorCode.SUCCESS,
                                       connection_handle=connection_handle,
-                                      reason=hci.DisconnectReason.REMOTE_USER_TERMINATED_CONNECTION))
+                                      reason=hci.ErrorCode.CONNECTION_TERMINATED_BY_LOCAL_HOST))
 
         # 9. Configure Lower Tester to use a public device address that differs from the IUT address in the
         # most significant octet as parameter of CONNECT_IND.
diff --git a/tools/rootcanal/test/LL/DDI/ADV/BV_26_C.py b/tools/rootcanal/test/LL/DDI/ADV/BV_26_C.py
new file mode 100644
index 0000000..f3b1365
--- /dev/null
+++ b/tools/rootcanal/test/LL/DDI/ADV/BV_26_C.py
@@ -0,0 +1,224 @@
+import asyncio
+import hci_packets as hci
+import link_layer_packets as ll
+import math
+import random
+import unittest
+from dataclasses import dataclass
+from hci_packets import ErrorCode, FragmentPreference
+from py.bluetooth import Address
+from py.controller import ControllerTest
+from typing import List
+
+
+@dataclass
+class TestRound:
+    data_length: int
+
+
+class Test(ControllerTest):
+    # Test parameters.
+    LL_advertiser_advInterval_MIN = 0x200
+    LL_advertiser_advInterval_MAX = 0x200
+    LL_advertiser_Adv_Channel_Map = 0x7
+    LL_initiator_connInterval = 0x200
+    LL_initiator_connPeripheralLatency = 0x200
+    LL_initiator_connSupervisionTimeout = 0x200
+
+    # LL/DDI/ADV/BV-26-C [Extended Advertising, Periodic Advertising – LE 1M PHY]
+    async def test(self):
+        controller = self.controller
+
+        if not controller.le_features.le_periodic_advertising:
+            self.skipTest("LE periodic advertising not supported")
+
+        # 1. The Upper Tester sends an HCI_LE_Read_Maximum_Advertising_Data_Length command to the
+        # IUT and receives a Maximum_Advertising_Data_Length between 0x001F and 0x0672 in return.
+        # The Upper Tester stores the Maximum_Advertising_Data_Length for future use.
+        # For each round from 1–6 based on Table 4.10.
+        controller.send_cmd(hci.LeReadMaximumAdvertisingDataLength())
+
+        event = await self.expect_cmd_complete(hci.LeReadMaximumAdvertisingDataLengthComplete)
+        maximum_advertising_data_length = event.maximum_advertising_data_length
+
+        # Test rounds.
+        test_rounds = [
+            TestRound(0),
+            TestRound(252),
+            TestRound(474),
+            TestRound(711),
+            TestRound(948),
+            TestRound(maximum_advertising_data_length),
+        ]
+
+        # 17. Repeat steps 2–16 for each Round shown in Table 4.10.
+        for test_round in test_rounds:
+            await self.steps_2_16(maximum_advertising_data_length, **vars(test_round))
+
+    async def steps_2_16(self, maximum_advertising_data_length: int, data_length: int):
+        controller = self.controller
+
+        # 2. If the Data Length listed in Table 4.10 for the current Round is less than or equal to the
+        # Maximum_Advertising_Data_Length proceed to step 3, otherwise skip to step 17.
+        if data_length > maximum_advertising_data_length:
+            return
+
+        # 3. The Upper Tester sends an HCI_LE_Set_Extended_Advertising_Parameters command to the
+        # IUT using all supported advertising channels and a selected advertising interval between the
+        # minimum and maximum advertising intervals supported. Advertising_Event_Properties parameter
+        # shall be set to 0x0000. The Primary_Advertising_PHY and Secondary_Advertising_PHY shall be
+        # set to the values specified in Table 4.9.
+        controller.send_cmd(
+            hci.LeSetExtendedAdvertisingParameters(advertising_handle=0,
+                                                   advertising_event_properties=hci.AdvertisingEventProperties(),
+                                                   primary_advertising_interval_min=self.LL_advertiser_advInterval_MIN,
+                                                   primary_advertising_interval_max=self.LL_advertiser_advInterval_MAX,
+                                                   primary_advertising_channel_map=self.LL_advertiser_Adv_Channel_Map,
+                                                   own_address_type=hci.OwnAddressType.PUBLIC_DEVICE_ADDRESS,
+                                                   advertising_filter_policy=hci.AdvertisingFilterPolicy.ALL_DEVICES,
+                                                   primary_advertising_phy=hci.PrimaryPhyType.LE_1M))
+
+        await self.expect_evt(
+            hci.LeSetExtendedAdvertisingParametersComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
+
+        # 4. The Upper Tester sends an HCI_LE_Set_Periodic_Advertising_Parameters command to the IUT
+        # using all supported advertising channels and selected periodic interval.
+        # Periodic_Advertising_Properties parameter shall be set to 0x0000.
+        controller.send_cmd(
+            hci.LeSetPeriodicAdvertisingParameters(advertising_handle=0,
+                                                   periodic_advertising_interval_min=0x100,
+                                                   periodic_advertising_interval_max=0x100,
+                                                   include_tx_power=False))
+
+        await self.expect_evt(
+            hci.LeSetPeriodicAdvertisingParametersComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
+
+        # 5. The Upper Tester sends one or more HCI_LE_Set_Periodic_Advertising_Data commands to the
+        # IUT with values according to Table 4.10 and using random octets from 1 to 254 as the payload. If
+        # the Data Length is greater than 252 the Upper Tester shall send multiple commands using one
+        # Operation 0x01 (First fragment) command, followed by zero or more Operation 0x00
+        # (Intermediate Fragment) commands, and a final Operation 0x02 (Last fragment) command.
+        # Otherwise the Upper Tester shall send a single command using Operation 0x03 (Complete Data).
+        advertising_data = [random.randint(1, 254) for n in range(data_length)]
+        num_fragments = math.ceil(data_length / 251) or 1  # Make sure to set the advertising data if it is empty.
+        for n in range(num_fragments):
+            fragment_offset = 251 * n
+            fragment_length = min(251, data_length - fragment_offset)
+            if num_fragments == 1:
+                operation = hci.Operation.COMPLETE_ADVERTISEMENT
+            elif n == 0:
+                operation = hci.Operation.FIRST_FRAGMENT
+            elif n == num_fragments - 1:
+                operation = hci.Operation.LAST_FRAGMENT
+            else:
+                operation = hci.Operation.INTERMEDIATE_FRAGMENT
+
+            controller.send_cmd(
+                hci.LeSetPeriodicAdvertisingDataRaw(advertising_handle=0,
+                                                    operation=operation,
+                                                    advertising_data=advertising_data[fragment_offset:fragment_offset +
+                                                                                      fragment_length]))
+
+            await self.expect_evt(
+                hci.LeSetPeriodicAdvertisingDataComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
+
+        # 6. The Upper Tester enables periodic advertising using the
+        # HCI_LE_Set_Periodic_Advertising_Enable command with the Enable parameter set to 0x01
+        # (Periodic Advertising).
+        controller.send_cmd(hci.LeSetPeriodicAdvertisingEnable(enable=True, include_adi=False, advertising_handle=0))
+
+        await self.expect_evt(
+            hci.LeSetPeriodicAdvertisingEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
+
+        # Note: no periodic advertising event is expected until extended
+        # advertising is also enabled for the advertising set.
+
+        # 7. The Upper Tester enables advertising using the HCI_LE_Set_Extended_Advertising_Enable
+        # command. The Duration[0] parameter is set to 0x0000 (No Advertising Duration).
+        controller.send_cmd(
+            hci.LeSetExtendedAdvertisingEnable(
+                enable=hci.Enable.ENABLED,
+                enabled_sets=[hci.EnabledSet(advertising_handle=0, duration=0, max_extended_advertising_events=0)]))
+
+        await self.expect_evt(
+            hci.LeSetExtendedAdvertisingEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
+
+        # 8. The Lower Tester receives an ADV_EXT_IND packet from the IUT with AdvMode set to 00b with
+        # the AuxPtr Extended Header field present.
+
+        # 9. The Lower Tester utilizes the AuxPtr field to listen for an AUX_ADV_IND PDU on the secondary
+        # advertising channel with the AdvMode field set to 00b and the SyncInfo Extended Header fields
+        # present.
+
+        # 10. The Lower Tester utilizes the SyncInfo field to listen for an AUX_SYNC_IND PDU on the
+        # secondary advertising channel using the index selected by the LE Channel Selection Algorithm
+        # #2 and synchronizes with the periodic advertisements. The AUX_SYNC_IND PDU shall have the
+        # AdvMode field set to 00b with no ADI field. If the AUX_SYNC_IND PDU AdvData field does not
+        # contain all the data submitted in step 5 (if any), it shall include an AuxPtr field.
+
+        # 11. If the AUX_SYNC_IND PDU contains an AuxPtr field, the Lower Tester utilizes it to listen for an
+        # AUX_CHAIN_IND PDU with the AdvMode field set to 00b and containing additional data
+        # submitted in step 5. If the AUX_CHAIN_IND PDU contains an AuxPtr field this step is repeated
+        # until an AUX_CHAIN_IND PDU is received with no AuxPtr field and all data has been received.
+
+        # 12. Repeat steps 8–11 100 times.
+        received = [0, 0]
+        for n in range(15):
+            index = await self.expect_ll([
+                ll.LeExtendedAdvertisingPdu(source_address=controller.address,
+                                            advertising_address_type=ll.AddressType.PUBLIC,
+                                            target_address_type=ll.AddressType.PUBLIC,
+                                            connectable=False,
+                                            scannable=False,
+                                            directed=False,
+                                            sid=0,
+                                            tx_power=0,
+                                            primary_phy=ll.PrimaryPhyType.LE_1M,
+                                            secondary_phy=ll.SecondaryPhyType.NO_PACKETS,
+                                            periodic_advertising_interval=0x100,
+                                            advertising_data=[]),
+                ll.LePeriodicAdvertisingPdu(source_address=controller.address,
+                                            advertising_address_type=ll.AddressType.PUBLIC,
+                                            sid=0,
+                                            tx_power=0,
+                                            advertising_interval=0x100,
+                                            advertising_data=advertising_data)
+            ])
+            received[index] = received[index] + 1
+
+        # Note: the extended advertising interval is twice the periodic
+        # advertising interval; the number of events received of each kind is
+        # deterministic.
+        self.assertTrue(received[0] == 5)
+        self.assertTrue(received[1] == 10)
+
+        # 13. The Upper Tester disables extended advertising using the
+        # HCI_LE_Set_Extended_Advertising_Enable command but maintains periodic advertising.
+        controller.send_cmd(hci.LeSetExtendedAdvertisingEnable(enable=hci.Enable.DISABLED, enabled_sets=[]))
+
+        await self.expect_evt(
+            hci.LeSetExtendedAdvertisingEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
+
+        # 14. The Lower Tester confirms that periodic advertising continues when extended advertising is
+        # disabled by repeating steps 10–11 100 times.
+        for n in range(10):
+            await self.expect_ll(
+                ll.LePeriodicAdvertisingPdu(source_address=controller.address,
+                                            advertising_address_type=ll.AddressType.PUBLIC,
+                                            sid=0,
+                                            tx_power=0,
+                                            advertising_interval=0x100,
+                                            advertising_data=advertising_data))
+
+        # 15. The Upper Tester disables periodic advertising using the
+        # HCI_LE_Set_Periodic_Advertising_Enable command.
+        controller.send_cmd(hci.LeSetPeriodicAdvertisingEnable(enable=False, include_adi=False, advertising_handle=0))
+
+        await self.expect_evt(
+            hci.LeSetPeriodicAdvertisingEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
+
+        # 16. The Upper Tester clears the advertising configuration using the HCI_LE_Clear_Advertising_Sets
+        # command.
+        controller.send_cmd(hci.LeClearAdvertisingSets())
+
+        await self.expect_evt(hci.LeClearAdvertisingSetsComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
diff --git a/tools/rootcanal/test/LL/DDI/SCN/BV_79_C.py b/tools/rootcanal/test/LL/DDI/SCN/BV_79_C.py
new file mode 100644
index 0000000..ff2ccba
--- /dev/null
+++ b/tools/rootcanal/test/LL/DDI/SCN/BV_79_C.py
@@ -0,0 +1,188 @@
+import hci_packets as hci
+import link_layer_packets as ll
+import math
+import random
+from dataclasses import dataclass
+from hci_packets import ErrorCode
+from py.bluetooth import Address
+from py.controller import ControllerTest
+from typing import Optional
+
+ADV_IND = 0x13
+ADV_DIRECT_IND = 0x15
+ADV_SCAN_IND = 0x12
+ADV_NONCONN_IND = 0x10
+ADV_EXT_IND = 0x0
+
+
+class Test(ControllerTest):
+
+    # LL/DDI/SCN/BV-79-C  [Extended Scanning, Passive, Periodic Advertising Report,
+    #                      RSSI and TX_Power – LE 1M PHY]
+    async def test(self):
+        # Test rounds.
+        # Note: some tests are skipped as no distinction is made between
+        # ADV_EXT_IND, AUX_ADV_IND, AUX_CHAIN_IND.
+        controller = self.controller
+        lower_tester_address = Address('11:22:33:44:55:66')
+        advertising_sid = 0x3
+        tx_power = 0x0a
+        periodic_advertising_interval = 0x100
+
+        # 1. The Upper Tester sends an HCI_LE_Set_Extended_Scan_Parameters command to the IUT with
+        # Scanning_PHYs set as specified in Table 4.35, Scan_Type[0] set to 0x00 (Passive Scanning),
+        # Scan_Interval[0] set to 0x0010, Scan_Window[0] set to 0x0010, Own_Address_Type set to 0x00
+        # (Public Device Address), and Scanning_Filter_Policy shall be set to 0x00 (Accept All) and
+        # receives a successful HCI_Command_Complete event in return.
+        controller.send_cmd(
+            hci.LeSetExtendedScanParameters(own_address_type=hci.OwnAddressType.PUBLIC_DEVICE_ADDRESS,
+                                            scanning_filter_policy=hci.LeScanningFilterPolicy.ACCEPT_ALL,
+                                            scanning_phys=0x1,
+                                            parameters=[
+                                                hci.PhyScanParameters(le_scan_type=hci.LeScanType.PASSIVE,
+                                                                      le_scan_interval=0x0010,
+                                                                      le_scan_window=0x0010)
+                                            ]))
+
+        await self.expect_evt(
+            hci.LeSetExtendedScanParametersComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
+
+        # 2. The Upper Tester sends an HCI_LE_Set_Extended_Scan_Enable command to the IUT to enable
+        # scanning with Filter_Duplicates, Duration, and Period are set to zero and receives a successful
+        # HCI_Command_Complete event in return.
+        controller.send_cmd(
+            hci.LeSetExtendedScanEnable(enable=hci.Enable.ENABLED,
+                                        filter_duplicates=hci.Enable.DISABLED,
+                                        duration=0,
+                                        period=0))
+
+        await self.expect_evt(hci.LeSetExtendedScanEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
+
+        for n in range(3):
+            # 3. The Lower Tester begins advertising using ADV_EXT_IND and AUX_ADV_IND PDUs. The
+            # ADV_EXT_IND PDUs include an AuxPtr that refers to the AUX_ADV_IND PDU on the secondary
+            # advertising channel. The AUX_ADV_IND PDUs include the AdvA field containing the Lower
+            # Tester address, and the SyncInfo field referring to the AUX_SYNC_IND PDU. The Lower Tester
+            # continues advertising until directed to stop in the test procedure.
+            controller.send_ll(ll.LeExtendedAdvertisingPdu(source_address=lower_tester_address,
+                                                           advertising_address_type=ll.AddressType.PUBLIC,
+                                                           connectable=False,
+                                                           scannable=False,
+                                                           directed=False,
+                                                           sid=advertising_sid,
+                                                           tx_power=tx_power,
+                                                           primary_phy=ll.PrimaryPhyType.LE_1M,
+                                                           secondary_phy=ll.SecondaryPhyType.NO_PACKETS,
+                                                           periodic_advertising_interval=0x100,
+                                                           advertising_data=[]),
+                               rssi=0x10)
+
+            # 4. The IUT sends an HCI_LE_Extended_Advertising_Report event to the Upper Tester containing a
+            # nonzero Periodic_Advertising_Interval, Data Status in the Event_Type[i] field set to the value
+            # 0b00 (Complete), and RSSI[i] set to a valid value.
+            await self.expect_evt(
+                hci.LeExtendedAdvertisingReportRaw(responses=[
+                    hci.LeExtendedAdvertisingResponseRaw(
+                        connectable=False,
+                        scannable=False,
+                        directed=False,
+                        scan_response=False,
+                        legacy=False,
+                        data_status=hci.DataStatus.COMPLETE,
+                        address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
+                        address=lower_tester_address,
+                        primary_phy=hci.PrimaryPhyType.LE_1M,
+                        secondary_phy=hci.SecondaryPhyType.NO_PACKETS,
+                        advertising_sid=advertising_sid,
+                        tx_power=tx_power,
+                        rssi=0x10,
+                        periodic_advertising_interval=periodic_advertising_interval,
+                        direct_address_type=hci.DirectAdvertisingAddressType.NO_ADDRESS_PROVIDED,
+                        direct_address=Address(),
+                        advertising_data=[])
+                ]))
+
+        # 5. The Upper Tester sends an HCI_LE_Periodic_Advertising_Create_Sync command to the IUT to
+        # synchronize with the Lower Tester’s periodic advertisements with Options set to 0x00 (Do not
+        # Use List), Advertising_SID set to the Advertising_SID from step 3, Advertiser_Address_Type set
+        # to 0x00 (Public Device Address), Advertiser_Address set to the Lower Tester’s address, Skip set
+        # to the value 0x0003, Sync_Timeout set to (Skip + 3) x Periodic_Advertising_Interval from step 4,
+        # and Sync_CTE_Type set to 0x00 and receives a successful HCI_Command_Complete event in
+        # return.
+        controller.send_cmd(
+            hci.LePeriodicAdvertisingCreateSync(
+                options=hci.PeriodicAdvertisingOptions(use_periodic_advertiser_list=False,
+                                                       disable_reporting=False,
+                                                       enable_duplicate_filtering=False),
+                advertising_sid=advertising_sid,
+                advertiser_address_type=hci.AdvertiserAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                advertiser_address=lower_tester_address,
+                skip=0x3,
+                sync_timeout=6 * periodic_advertising_interval,
+                sync_cte_type=0))
+
+        await self.expect_evt(
+            hci.LePeriodicAdvertisingCreateSyncStatus(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
+
+        # 6. The Lower Tester generates an AUX_SYNC_IND PDU on the secondary advertising channel with
+        # AuxPtr set to a value referring to the first AUX_CHAIN_IND PDU in the train, TxPower set to 10,
+        # and AdvData set to N octets of random data.
+        controller.send_ll(
+            ll.LePeriodicAdvertisingPdu(source_address=lower_tester_address,
+                                        advertising_address_type=ll.AddressType.PUBLIC,
+                                        sid=advertising_sid,
+                                        tx_power=tx_power,
+                                        advertising_interval=periodic_advertising_interval,
+                                        advertising_data=[]))
+
+        # 7. The IUT sends a successful HCI_LE_Periodic_Advertising_Sync_Established event to the Upper
+        # Tester containing a Status of 0x00 (Success), Sync_Handle set to a valid value, and the
+        # Advertising_SID received in step 3.
+        await self.expect_evt(
+            hci.LePeriodicAdvertisingSyncEstablished(
+                status=ErrorCode.SUCCESS,
+                sync_handle=0,
+                advertising_sid=advertising_sid,
+                advertiser_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
+                advertiser_address=lower_tester_address,
+                advertiser_phy=hci.SecondaryPhyType.LE_1M,
+                periodic_advertising_interval=periodic_advertising_interval,
+                advertiser_clock_accuracy=hci.ClockAccuracy.PPM_500,
+            ))
+
+        for n in range(3):
+            advertising_data_length = 256
+            advertising_data = [random.randint(1, 254) for n in range(advertising_data_length)]
+
+            # 8. The Lower Tester sends two AUX_CHAIN_IND PDUs to the IUT with AdvData set to min(249,
+            # (Scan_Max_Data – N) / 2) octets of random data for each AUX_CHAIN_IND PDU and the
+            # TxPower value of the AUX_CHAIN_IND PDUs set to 15. The PDUs should be sent as far apart
+            # as practical.
+            controller.send_ll(ll.LePeriodicAdvertisingPdu(source_address=lower_tester_address,
+                                                           advertising_address_type=ll.AddressType.PUBLIC,
+                                                           sid=advertising_sid,
+                                                           tx_power=tx_power,
+                                                           advertising_interval=periodic_advertising_interval,
+                                                           advertising_data=advertising_data),
+                               rssi=0x10)
+
+            # 9. The IUT sends multiple HCI_LE_Periodic_Advertising_Report events to the Upper Tester with
+            # Data Status in the Event_Type[i] field set to 0b01 (Incomplete, more data to come), TX_Power[i]
+            # set to the value of the TxPower field for the AUX_SYNC_IND received in step 6, and RSSI[i] set
+            # to a valid value. Subsequent reports with data and the status set to “Incomplete, more data to
+            # come” or “complete” can have the TX_Power field set to 0x7F.
+            offset = 0
+            max_fragment_length = 247
+            num_fragments = math.ceil(advertising_data_length / max_fragment_length) or 1
+            for n in range(num_fragments):
+                remaining_length = advertising_data_length - offset
+                fragment_length = min(max_fragment_length, remaining_length)
+                data_status = hci.DataStatus.CONTINUING if remaining_length > max_fragment_length else hci.DataStatus.COMPLETE
+                await self.expect_evt(
+                    hci.LePeriodicAdvertisingReport(sync_handle=0,
+                                                    tx_power=tx_power,
+                                                    rssi=0x10,
+                                                    cte_type=hci.CteType.NO_CONSTANT_TONE_EXTENSION,
+                                                    data_status=data_status,
+                                                    data=advertising_data[offset:offset + fragment_length]))
+                offset += fragment_length
diff --git a/tools/rootcanal/test/main.py b/tools/rootcanal/test/main.py
index d3062cd..aefd3d4 100644
--- a/tools/rootcanal/test/main.py
+++ b/tools/rootcanal/test/main.py
@@ -21,15 +21,16 @@
     'LL.DDI.ADV.BV_17_C',
     'LL.DDI.ADV.BV_18_C',
     'LL.DDI.ADV.BV_19_C',
-    # TODO: Implement HCI command Le Set Default Phy
-    # 'LL.DDI.ADV.BV_20_C',
+    'LL.DDI.ADV.BV_20_C',
     'LL.DDI.ADV.BV_21_C',
     'LL.DDI.ADV.BV_22_C',
+    'LL.DDI.ADV.BV_26_C',
     'LL.DDI.ADV.BV_47_C',
     'LL.DDI.SCN.BV_13_C',
     'LL.DDI.SCN.BV_14_C',
     'LL.DDI.SCN.BV_18_C',
     'LL.DDI.SCN.BV_19_C',
+    'LL.DDI.SCN.BV_79_C',
 ]
 
 if __name__ == "__main__":