hci_vs_sdc_compat_mode_window_offset_set causing Assertion 57, 2024

I'm running the central_hci example, initially on an nRF54LM20 DK and then on an nRF52840 DK. I'm trying to pair with a Redragon Antonium Pro 108 keyboard. The sniffer capture indicates that it never gets past the CONNECT_IND. Comparing this with a pairing I was able to successfully perform with my phone indicates that the only difference in the CONNECT_IND is the transmitWindowOffset, which is 0 on the DK.

I found the SoftDevice Controller compatibility-mode window-offset command, hci_vs_sdc_compat_mode_window_offset_set, and tried this using version 3.4.1 of the SDK on the 54 DK, but hit an asserttion 57, 2024. I then started going back in SDK versions and using the nRF52840 as well. I had to use the direct SDC API function, sdc_hci_cmd_vs_compat_mode_window_offset_set, on earlier versions since the hci_vs_sdc_compat_mode_window_offset_set wasn't supported earlier. The direct call succeeds on NCS v2.4.0 but crashes on later releases.

The test calls sdc_hci_cmd_vs_compat_mode_window_offset_set() after bt_enable() and holds the NCS multithreading lock around the call. The parameter struct is initialized with .enable = 0 or .enable = 1; it contains no other fields.

Results:

NCS v2.4.0, nRF52840 DK: returns 0 for both enable = 0 and enable = 1.
NCS v2.7.0, nRF52840 DK: crashes. I have not captured the assertion ID yet.
NCS v2.9.3, nRF52840 DK: crashes. I have not captured the assertion ID yet.
NCS v3.2.0, nRF52840 DK: direct call with enable = 1 asserts with 57, 2530.
NCS v3.4.1, nRF54LM20 DK: assertion 57, 2024 observed; the assertion also occurs with enable = 0.
An HCI-wrapper call in NCS v3.2.0 returns -5, but inspection of that release’s NCS HCI dispatcher shows the opcode is not routed to SDC there. I understand this result is separate from the direct-call assertion.

The SDC header says the APIs in sdc_hci_vs.h are expected to be called at the same execution priority as mpsl_low_priority_process(). The direct test runs in the application thread after bt_enable() while holding the SDK multithreading lock. Could you confirm whether that call context is supported for this API, or whether it must run specifically at the MPSL low-priority execution priority? If the context is supported, could you decode assertion IDs 57, 2530 and 57, 2024 and advise which release introduced the failure?

I have attached the test app main.c file.

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <mpsl/mpsl_work.h>
#include <sdc_hci_vs.h>

int multithreading_lock_acquire(k_timeout_t timeout);
void multithreading_lock_release(void);

static const sdc_hci_cmd_vs_compat_mode_window_offset_set_t params = {
	.enable = 1,
};
static struct k_work compat_mode_work;
static struct k_sem compat_mode_done;
static int compat_mode_lock_error;
static int compat_mode_result;

static void compat_mode_work_handler(struct k_work *work)
{
	ARG_UNUSED(work);
	printk("MPSL work handler entered\n");

	compat_mode_lock_error = multithreading_lock_acquire(K_NO_WAIT);
	if (!compat_mode_lock_error) {
		printk("About to call SDC compat-mode API (enable=%u)\n",
		       (unsigned int)params.enable);
		compat_mode_result = sdc_hci_cmd_vs_compat_mode_window_offset_set(&params);
		multithreading_lock_release();
	}
	k_sem_give(&compat_mode_done);
}

int main(void)
{
	int err;

	printk("Enabling Bluetooth\n");
	err = bt_enable(NULL);
	if (err) {
		printk("bt_enable failed: %d\n", err);
		return 0;
	}
	printk("Bluetooth enabled\n");

	k_sem_init(&compat_mode_done, 0, 1);
	k_work_init(&compat_mode_work, compat_mode_work_handler);
	printk("Submitting SDC window-offset command to MPSL workqueue (enable=%u)\n",
	       (unsigned int)params.enable);
	mpsl_work_submit(&compat_mode_work);
	err = k_sem_take(&compat_mode_done, K_FOREVER);
	if (err) {
		printk("Waiting for MPSL work item failed: %d\n", err);
		return 0;
	}
	if (compat_mode_lock_error) {
		printk("Controller lock unavailable in MPSL work item: %d\n",
		       compat_mode_lock_error);
	} else {
		printk("MPSL workqueue SDC call returned: %d\n", compat_mode_result);
	}

	return 0;
}

Related