This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

softdevice controller use

Hi,

I'm trying to make the softdevice controller work on NuttX RTOS. I followed the SDK example code to reproduce initialization. All calls are returning 0 (no error). However, once I send the first HCI command (a controller RESET) I don't receive SWI interrupt which would lead to signaling for available HCI EVT. If I call sdc_hci_evt_get() on my own, it returns 0 (indicating available EVT), so I think the SWI signaling is not working. I'm also not receiving any other interrupts configured (for RTC, TIMER, RADIO, etc). If I monitor peripheral registers after initialization calls, I also don't see any registers related to peripheral interrupt change. I also verified that if I manually pend the chosen SWI line I get to the appropriate ISR. I'm not sure if I should be doing something else besides these initialization calls (do I need to enable events for each peripheral?). The relevant code is as follows:

int nrf52_sdc_initialize(void)
{
  int ret;
  int32_t required_memory;
  sdc_cfg_t cfg;

  /* Initialize device data */

  memset(&g_sdc_dev, 0, sizeof(g_sdc_dev));
  nxsem_init(&g_sdc_dev.exclsem, 0, 1);

  /* Register interrupt handler for normal-priority events */

  irq_attach(NRF52_IRQ_SWI5_EGU5, swi_isr, NULL);
  irq_attach(NRF52_IRQ_POWER_CLOCK, power_clock_isr, NULL);
  irq_attach(NRF52_IRQ_RNG, rng_isr, NULL);

  up_enable_irq(NRF52_IRQ_SWI5_EGU5);
  up_enable_irq(NRF52_IRQ_POWER_CLOCK);
  up_enable_irq(NRF52_IRQ_RNG);

  up_prioritize_irq(NRF52_IRQ_SWI5_EGU5, NVIC_SYSH_PRIORITY_DEFAULT);
  up_prioritize_irq(NRF52_IRQ_POWER_CLOCK, NVIC_SYSH_PRIORITY_DEFAULT);
  up_prioritize_irq(NRF52_IRQ_RNG, NVIC_SYSH_PRIORITY_DEFAULT);

  // TODO: RNG handler should be called with normal priority but without
  // added processing, is it OK to use NuttX forwarding then?
  // Otherwise, is it OK to do as below but use normal priority?

  /* TODO: how do WFI again after high priority interrupt wakes MCU up? */

  /* Register high-priority interrupts for specific peripherals */

  arm_ramvec_attach(NRF52_IRQ_RTC0, rtc0_handler);
  arm_ramvec_attach(NRF52_IRQ_TIMER0, timer0_handler);
  arm_ramvec_attach(NRF52_IRQ_RADIO, radio_handler);

  up_prioritize_irq(NRF52_IRQ_RTC0, MPSL_HIGH_IRQ_PRIORITY);
  up_prioritize_irq(NRF52_IRQ_TIMER0, MPSL_HIGH_IRQ_PRIORITY);
  up_prioritize_irq(NRF52_IRQ_RADIO, MPSL_HIGH_IRQ_PRIORITY);

  up_enable_irq(NRF52_IRQ_RTC0);
  up_enable_irq(NRF52_IRQ_TIMER0);
  up_enable_irq(NRF52_IRQ_RADIO);

  /* Initialize MPSL */

  ret = mpsl_init(&g_clock_config, SWI5_EGU5_IRQn, &mpsl_assert_handler);

  if (ret < 0)
    {
      wlerr("mpsl init failed: %d\n", ret);
      return ret;
    }

  /* Initialize SDC */

  ret = sdc_init(&sdc_fault_handler);

  if (ret < 0)
    {
      wlerr("mpsl init failed: %d\n", ret);
      return ret;
    }

  cfg.master_count.count = SDC_MASTER_COUNT;
  ret = sdc_cfg_set(SDC_DEFAULT_RESOURCE_CFG_TAG,
                    SDC_CFG_TYPE_MASTER_COUNT, &cfg);

  if (ret < 0)
    {
      wlerr("Failed to set master role count: %d\n", ret);
      return ret;
    }

  cfg.slave_count.count = CONFIG_NRF52_SDC_SLAVE_COUNT;
  ret = sdc_cfg_set(SDC_DEFAULT_RESOURCE_CFG_TAG,
                    SDC_CFG_TYPE_SLAVE_COUNT, &cfg);

  if (ret < 0)
    {
      wlerr("Failed to set slave role count: %d\n", ret);
      return ret;
    }

  cfg.buffer_cfg.rx_packet_size = SDC_DEFAULT_RX_PACKET_SIZE;
  cfg.buffer_cfg.tx_packet_size = SDC_DEFAULT_TX_PACKET_SIZE;
  cfg.buffer_cfg.rx_packet_count = SDC_DEFAULT_RX_PACKET_COUNT;
  cfg.buffer_cfg.tx_packet_count = SDC_DEFAULT_TX_PACKET_COUNT;

  required_memory =
      sdc_cfg_set(SDC_DEFAULT_RESOURCE_CFG_TAG,
                  SDC_CFG_TYPE_BUFFER_CFG, &cfg);

  if (required_memory < 0)
    {
      wlerr("Failed to set packet size/count: %ld\n", required_memory);
      return ret;
    }

  ASSERT(required_memory <= sizeof(g_sdc_dev.mempool));

  /* Turn on specific features */

#ifdef CONFIG_NRF52_SDC_ADVERTISING
  ret = sdc_support_adv();

  if (ret < 0)
    {
      wlerr("Could not enable advertising feature: %d\n", ret);
      return ret;
    }
#endif

#ifdef CONFIG_NRF52_SDC_SCANNING
  ret = sdc_support_scan();

  if (ret < 0)
    {
      wlerr("Could not enable scanning feature: %d\n", ret);
      return ret;
    }
#endif

#ifdef CONFIG_NRF52_SDC_CONNECTION
  ret = sdc_support_master();

  if (ret < 0)
    {
      wlerr("Could not enable master feature: %d\n", ret);
      return ret;
    }
#endif

#if CONFIG_NRF52_SDC_SLAVE_COUNT > 0
  ret = sdc_support_slave();

  if (ret < 0)
    {
      wlerr("Could not enable slave feature: %d\n", ret);
      return ret;
    }
#endif

  ret = sdc_enable(sdc_hci_signal, g_sdc_dev.mempool);

  if (ret < 0)
    {
      wlerr("SoftDevice controller enable failed: %d\n", ret);
      return ret;
    }

  /* Register network device */

  ret = bt_netdev_register(&g_bt_driver);

  return ret;
}

Thank you,

Matias

Parents Reply Children
  • Yes, that is part of normal NuttX boot. As I mentioned, I can manually trigger an interrupt (up_trigger_irq) and it will go to the handler as expected. The problems is that SDC does not seem to be interacting with MPSL so that MPSL itself triggers the IRQ.

  • I continued testing by manually calling by sd_hci_signal() handler, which let me complete host layer initialization by receiving all "command complete" events that were being generated during setup. Once I started advertising I got the SWI5 interrupt.

    I'm beginning to think that this signaling is not used for all HCI events or maybe there's some HCI command which triggers the signaling to start. Or maybe there's some issue in MPSL/SDC?

  • Actually I see that the example code mostly does the same: it triggers event handling right after sending, by posting the same semaphore that is posted when the signal is received from MPSL:

    static int cmd_handle(struct net_buf *cmd)
    {
            BT_DBG("");
    
            int errcode = MULTITHREADING_LOCK_ACQUIRE();
    
            if (!errcode) {
                    errcode = hci_internal_cmd_put(cmd->data);
                    MULTITHREADING_LOCK_RELEASE();
            }
            if (errcode) {
                    return errcode;
            }
    
            k_sem_give(&sem_recv);
    
            return 0;
    }

    and

    void host_signal(void)
    {
            /* Wake up the RX event/data thread */
            k_sem_give(&sem_recv);
    }

    It would be good if you could clarify as to why this is needed. In other words, what kind of events will trigger the signal and which will not. Also, I think that this should be better documented as it is not evident from the documentation.

Related