From ab0e5801a244bb7c35c48b75a73a82b894e83f4f Mon Sep 17 00:00:00 2001 From: TzuWei Date: Mon, 13 Jul 2026 21:15:28 +0800 Subject: [PATCH 1/2] Revert "CIG: Fix CIG parameters for unidirectional CIS" --- bumble/device.py | 12 ------------ tests/device_test.py | 19 ------------------- 2 files changed, 31 deletions(-) diff --git a/bumble/device.py b/bumble/device.py index 7eb5dc15..ef8fe481 100644 --- a/bumble/device.py +++ b/bumble/device.py @@ -1576,18 +1576,6 @@ class CisParameters: rtn_c_to_p: int = DEVICE_DEFAULT_ISO_CIS_RTN # Number of C->P retransmissions rtn_p_to_c: int = DEVICE_DEFAULT_ISO_CIS_RTN # Number of P->C retransmissions - def __post_init__(self) -> None: - # For unidirectional CIS (e.g., Central-to-Peripheral only), the unused direction's - # SDU size is 0. If SDU size is 0, the corresponding retransmission count and PHY - # must also be set to 0. Otherwise, some controllers will reject the parameters - # with error 0x30 (Parameter Out Of Mandatory Range). - if self.max_sdu_c_to_p == 0: - self.rtn_c_to_p = 0 - self.phy_c_to_p = hci.PhyBit(0) - if self.max_sdu_p_to_c == 0: - self.rtn_p_to_c = 0 - self.phy_p_to_c = hci.PhyBit(0) - cig_id: int cis_parameters: list[CisParameters] sdu_interval_c_to_p: int # C->P SDU interval, in microseconds diff --git a/tests/device_test.py b/tests/device_test.py index 7df82802..c8904f7f 100644 --- a/tests/device_test.py +++ b/tests/device_test.py @@ -596,25 +596,6 @@ def on_hci_le_cis_established_event(host, event): await asyncio.wait_for(cis_create_task, _TIMEOUT) -# ----------------------------------------------------------------------------- -def test_cis_parameters_unidirectional(): - # Test C2P unidirectional (P to C not used) - cis_c2p = CigParameters.CisParameters(cis_id=1, max_sdu_p_to_c=0) - assert cis_c2p.max_sdu_c_to_p != 0 - assert cis_c2p.rtn_c_to_p != 0 - assert cis_c2p.phy_c_to_p != hci.PhyBit(0) - assert cis_c2p.rtn_p_to_c == 0 - assert cis_c2p.phy_p_to_c == hci.PhyBit(0) - - # Test P2C unidirectional (C to P not used) - cis_p2c = CigParameters.CisParameters(cis_id=2, max_sdu_c_to_p=0) - assert cis_p2c.max_sdu_p_to_c != 0 - assert cis_p2c.rtn_p_to_c != 0 - assert cis_p2c.phy_p_to_c != hci.PhyBit(0) - assert cis_p2c.rtn_c_to_p == 0 - assert cis_p2c.phy_c_to_p == hci.PhyBit(0) - - # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_enter_and_exit_sniff_mode(): From 8b2bd0639370b0a922ebd4d9da9f2a550f8d9e47 Mon Sep 17 00:00:00 2001 From: uier Date: Tue, 14 Jul 2026 07:48:12 +0000 Subject: [PATCH 2/2] Force RNT to 0 for unidirectional CIS, keeping PHY valid --- bumble/device.py | 9 +++++++++ tests/device_test.py | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/bumble/device.py b/bumble/device.py index ef8fe481..32b06b8c 100644 --- a/bumble/device.py +++ b/bumble/device.py @@ -1576,6 +1576,15 @@ class CisParameters: rtn_c_to_p: int = DEVICE_DEFAULT_ISO_CIS_RTN # Number of C->P retransmissions rtn_p_to_c: int = DEVICE_DEFAULT_ISO_CIS_RTN # Number of P->C retransmissions + def __post_init__(self) -> None: + # For unidirectional CIS (e.g., Central-to-Peripheral only), the unused direction's + # SDU size is 0. If SDU size is 0, we set RTN to 0 as well for maintaining + # compatibility with older firmware that has error 0x30 bug. + if self.max_sdu_c_to_p == 0: + self.rtn_c_to_p = 0 + if self.max_sdu_p_to_c == 0: + self.rtn_p_to_c = 0 + cig_id: int cis_parameters: list[CisParameters] sdu_interval_c_to_p: int # C->P SDU interval, in microseconds diff --git a/tests/device_test.py b/tests/device_test.py index c8904f7f..fc21bf97 100644 --- a/tests/device_test.py +++ b/tests/device_test.py @@ -596,6 +596,25 @@ def on_hci_le_cis_established_event(host, event): await asyncio.wait_for(cis_create_task, _TIMEOUT) +# ----------------------------------------------------------------------------- +def test_cis_parameters_unidirectional(): + # Test C2P unidirectional (P to C not used) + cis_c2p = CigParameters.CisParameters(cis_id=1, max_sdu_p_to_c=0) + assert cis_c2p.max_sdu_c_to_p != 0 + assert cis_c2p.rtn_c_to_p != 0 + assert cis_c2p.phy_c_to_p != hci.PhyBit(0) + assert cis_c2p.rtn_p_to_c == 0 + assert cis_c2p.phy_p_to_c != hci.PhyBit(0) + + # Test P2C unidirectional (C to P not used) + cis_p2c = CigParameters.CisParameters(cis_id=2, max_sdu_c_to_p=0) + assert cis_p2c.max_sdu_p_to_c != 0 + assert cis_p2c.rtn_p_to_c != 0 + assert cis_p2c.phy_p_to_c != hci.PhyBit(0) + assert cis_p2c.rtn_c_to_p == 0 + assert cis_p2c.phy_c_to_p != hci.PhyBit(0) + + # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_enter_and_exit_sniff_mode():