BLE 1M+Coded PHY connection and scanning

I have a project where I want to connect one central device to multiple peripheral devices using BLE. Some of the connections should use 1M PHY, some should use coded PHY. Any of the peripheral devices may be present and the connections should form in any order - in other words, I want to scan and connect for both PHYs at the same time rather than one at a time (although, if needed, one at a time could be OK).

I am finding that I am able to successfully scan for devices advertising on both PHYs, but after connecting to one of the devices using coded PHY I can no longer see any coded PHY devices in the scan. I still get scan callbacks, but only for 1M PHY devices (I have multiple peripheral devices on both PHYs so I'm no longer seeing the non-connected ones). If the coded connection drops the coded scans start to work again. This is a problem because it limits me to only finding / connecting to one coded PHY device.

For reference here's some snippets showing the ad / scan / connect parameters.

Advertising:

uint32_t opt = BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_ONE_TIME | BT_LE_ADV_OPT_EXT_ADV | BT_LE_ADV_OPT_CODED;
struct bt_le_adv_param * adv_param = BT_LE_ADV_PARAM(opt, 160, 320, NULL);
int res = bt_le_ext_adv_update_param(ad_set, adv_param);
Scanning:
// Not shown: registering callbacks 
struct bt_le_scan_param scan_params =
{
    .type = BT_LE_SCAN_TYPE_PASSIVE,
    .options = BT_LE_SCAN_OPT_CODED,
    .interval = 0x40, // 40ms
    .window = 0x20, // 20ms
    .timeout = 0,
    .interval_coded = 0,
    .window_coded = 0,
};
int res = bt_le_scan_start(&scan_params, NULL);
Connecting (after getting the scan callback):
struct bt_conn_le_create_param create_param =
{
    .options = BT_CONN_LE_OPT_CODED,
    .interval = 0x40,
    .window = 0x20,
    .interval_coded = 0,
    .window_coded = 0,
    .timeout = 0, // Use default (3 s)
};
struct bt_le_conn_param conn_param =
{
    .interval_min = 6, // 7.5 ms, minimum allowed by spec
    .interval_max = 6, // 7.5 ms. Note: 6 <= interval_min <= interval_max <= 3200
    .latency = 0,
    .timeout = 400, // 4 seconds
};
int res = bt_conn_le_create(peer, create_param, conn_param, conn);
// Not shown: saving the connection handle
As mentioned, with the code above I can get one connection on coded PHY (verified with a BLE sniffer) but while that connection is active I no longer see scan callbacks for coded PHY devices. However, if I enable only coded PHY then it works as expected - I can run multiple coded connections and continue scanning for more (same if it's only 1M PHY). It seems to be only mixing 1M and coded that causes the issue. That is, setting the options as follows:
Advertising: no change
Scanning: .options = BT_LE_SCAN_OPT_CODED | BT_LE_SCAN_OPT_NO_1M,
Connecting: .options = BT_CONN_LE_OPT_CODED | BT_CONN_LE_OPT_NO_1M,
In the course of testing the 1M + coded setup, I also noticed that if I bypass scanning and connect directly using hard-coded MAC address, I can connect and run multiple connections as expected. So it seems like scanning is what is not working as expected.
Further, I noticed that the controller gives errors in some cases when two PHYs are used. In the above situations I don't see the errors, but if I increase the scan window above half of the scan interval, like this:
struct bt_le_scan_param scan_params =
{
    .type = BT_LE_SCAN_TYPE_PASSIVE,
    .options = BT_LE_SCAN_OPT_CODED,
    .interval = 0x40, // 40ms
    .window = 0x30, // 30ms
    .timeout = 0,
    .interval_coded = 0,
    .window_coded = 0,
};
then, I get the following error which I understand to mean 'invalid scan parameters':
<wrn> bt_hci_core: opcode 0x2041 status 0x12
I don't have a good understanding of how the settings for 1M and coded interact but my intuition is that this error occurs the scan interval is somehow shared between the PHYs, so if the 1M window + coded window is greater than the interval an error occurs. But this doesn't completely make sense as there are two separate intervals in play here, so I would expect the 1M interval and window validity to be independent of the coded interval and window.
So, I'm looking for any insight into all of this. Any help is appreciated!
1. Does this scanning behavior with 1M + coded make sense? Is there some limitation to scanning behavior when using both PHYs and also connecting to a device? How can I get scan callbacks for both 1M and coded PHY devices while also maintaining connections on both PHYs?
2. How specifically does scanning work when both 1M and coded interval and window params are provided? Does it alternate between scanning on 1M and then coded, or does it do them simultaneously? What limitations are there on the parameters I provide when both PHYs are in play?

Other relevant details:

NCS 2.4.1

5340 app core configuration (BT only):

CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y

CONFIG_BT_MAX_CONN=4

CONFIG_BT_EXT_ADV=y

CONFIG_BT_BUF_ACL_TX_SIZE=251
CONFIG_BT_BUF_ACL_TX_COUNT=10
CONFIG_BT_BUF_ACL_RX_SIZE=251

CONFIG_BT_RX_STACK_SIZE=2048
CONFIG_BT_FILTER_ACCEPT_LIST=y
CONFIG_BT_USER_PHY_UPDATE=y
CONFIG_BT_USER_DATA_LEN_UPDATE=y
CONFIG_BT_SMP=y
CONFIG_BT_DEVICE_NAME="ABC"

CONFIG_BT_ATT_PREPARE_COUNT=2
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=n

CONFIG_BT_L2CAP_TX_MTU=247
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y

CONFIG_BT_SCAN=y
CONFIG_BT_SCAN_FILTER_ENABLE=y
CONFIG_BT_SCAN_UUID_CNT=1
5340 net core configuration (BT only):
CONFIG_BT_MAX_CONN=4

CONFIG_BT_BUF_ACL_TX_SIZE=251
CONFIG_BT_BUF_ACL_RX_SIZE=251

CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
CONFIG_BT_CTLR_ADV_EXT=y
CONFIG_BT_CTLR_PHY_CODED=y

CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT=1
Parents Reply Children
Related