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
  • Hi,

    I don't receive SWI interrupt which would lead to signaling for available HCI EVT.

    The callback function you pass to sdc_enable() will be executed when HCI data or and HCI event is available. The callback will be executed in the same context as mpsl_low_priority_process.

    From your code snippet, your callback function is named sdc_hci_signal(). Does sdc_hci_signal() execute/trigger when send the first HCI command?

  • No, that handler is not called (even when I can manually verify that there's a pending HCI event). I understand that to trigger that call, mpsl should pend the configured interrupt (in this case, SWI5) from which I would call mpsl_low_priority_process(), but that interrupt is not triggered. Finally, If I call mpsl_low_priority_process() manually, the handler passed to sdc_enable() is not called either. So it would seem that mpsl is not aware that sdc has a pending HCI event.

  • Do you know if up_irqinitialize() is being called somewhere in your project/code ?

  • 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.

Reply
  • 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.

Children
No Data
Related