btashton commented on a change in pull request #2735: URL: https://github.com/apache/incubator-nuttx/pull/2735#discussion_r565844647
########## File path: arch/arm/src/nrf52/nrf52_sdc.c ########## @@ -0,0 +1,609 @@ +/**************************************************************************** + * arch/arm/src/chip/nrf52_sdc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/net/bluetooth.h> +#include <nuttx/wireless/bluetooth/bt_hci.h> +#include <nuttx/wireless/bluetooth/bt_driver.h> +#include <nuttx/irq.h> +#include <nuttx/arch.h> +#include <arch/armv7-m/nvicpri.h> +#include <arch/nrf52/nrf52_irq.h> +#include <nuttx/wqueue.h> + +#include "arm_internal.h" +#include "ram_vectors.h" +#include "arm_arch.h" + +#include <mpsl.h> +#include <sdc.h> +#include <sdc_hci.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if defined(CONFIG_SDC_SLAVE_COUNT) && \ + CONFIG_SDC_SLAVE_COUNT > CONFIG_BLUETOOTH_MAX_CONN +# error "Cannot support more BLE slave roles than connections" +#endif + +#define SDC_MASTER_COUNT (CONFIG_BLUETOOTH_MAX_CONN - \ + CONFIG_NRF52_SDC_SLAVE_COUNT) + +/* Todo: check central/peripheral against master/slave count */ + +#define MASTER_MEM_SIZE (SDC_MEM_PER_MASTER_LINK( \ + SDC_DEFAULT_TX_PACKET_SIZE, \ + SDC_DEFAULT_RX_PACKET_SIZE, \ + SDC_DEFAULT_TX_PACKET_COUNT, \ + SDC_DEFAULT_RX_PACKET_COUNT) \ + + SDC_MEM_MASTER_LINKS_SHARED) + +#define SLAVE_MEM_SIZE (SDC_MEM_PER_SLAVE_LINK( \ + SDC_DEFAULT_TX_PACKET_SIZE, \ + SDC_DEFAULT_RX_PACKET_SIZE, \ + SDC_DEFAULT_TX_PACKET_COUNT, \ + SDC_DEFAULT_RX_PACKET_COUNT) \ + + SDC_MEM_SLAVE_LINKS_SHARED) + +#define MEMPOOL_SIZE ((CONFIG_NRF52_SDC_SLAVE_COUNT * SLAVE_MEM_SIZE) + \ + (SDC_MASTER_COUNT * MASTER_MEM_SIZE)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nrf52_sdc_dev_s +{ + uint8_t mempool[MEMPOOL_SIZE]; + uint8_t msg_buffer[HCI_MSG_BUFFER_MAX_SIZE]; + + sem_t exclsem; + struct work_s work; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void mpsl_assert_handler(const char *const file, const uint32_t line); +static void sdc_fault_handler(const char *file, const uint32_t line); + +static int bt_open(FAR const struct bt_driver_s *btdev); +static int bt_hci_send(FAR const struct bt_driver_s *btdev, + FAR struct bt_buf_s *buf); + +static void on_hci(void); +static void on_hci_worker(void *arg); + +static void low_prio_worker(void *arg); + +static int swi_isr(int irq, FAR void *context, FAR void *arg); +static int power_clock_isr(int irq, FAR void *context, FAR void *arg); + +static void rng_handler(void); +static void rtc0_handler(void); +static void timer0_handler(void); +static void radio_handler(void); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct bt_driver_s g_bt_driver = +{ + .head_reserve = 0, + .open = bt_open, + .send = bt_hci_send +}; + +static const mpsl_clock_lfclk_cfg_t g_clock_config = +{ + .source = MPSL_CLOCK_LF_SRC_XTAL, + .rc_ctiv = 0, + .rc_temp_ctiv = 0, + .accuracy_ppm = CONFIG_NRF52_SDC_CLOCK_ACCURACY, + .skip_wait_lfclk_started = false +}; + +static struct nrf52_sdc_dev_s g_sdc_dev; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: bt_open + ****************************************************************************/ + +static int bt_open(FAR const struct bt_driver_s *btdev) +{ + return 0; +} + +/**************************************************************************** + * Name: bt_open + ****************************************************************************/ + +static int bt_hci_send(FAR const struct bt_driver_s *btdev, + FAR struct bt_buf_s *buf) +{ + int ret = OK; + irqstate_t flags; + + /* Pass HCI CMD/DATA to SDC */ + + if (buf->type == BT_CMD) + { + struct bt_hci_cmd_hdr_s *cmd = (struct bt_hci_cmd_hdr_s *)buf->data; + + wlinfo("passing CMD %d to softdevice\n", cmd->opcode); + + /* Ensure non-concurrent access to SDC operations */ + + flags = enter_critical_section(); + + nxsem_wait_uninterruptible(&g_sdc_dev.exclsem); + + if (sdc_hci_cmd_put(buf->data) < 0) + { + wlerr("sdc_hci_cmd_put() failed\n"); + ret = -EIO; + } + + nxsem_post(&g_sdc_dev.exclsem); + + leave_critical_section(flags); + + work_queue(LPWORK, &g_sdc_dev.work, on_hci_worker, NULL, 0); + } + else if (buf->type == BT_ACL_OUT) + { + wlinfo("passing ACL to softdevice\n"); + + /* Ensure non-concurrent access to SDC operations */ + + flags = enter_critical_section(); + + nxsem_wait_uninterruptible(&g_sdc_dev.exclsem); + + if (sdc_hci_data_put(buf->data) < 0) + { + wlerr("sdc_hci_data_put() failed\n"); + ret = -EIO; + } + + nxsem_post(&g_sdc_dev.exclsem); + + leave_critical_section(flags); + + work_queue(LPWORK, &g_sdc_dev.work, on_hci_worker, NULL, 0); + } + + if (ret < 0) + { + wlerr("bt_hci_send() failed: %d\n", ret); + return ret; + } + else + { + return buf->len; + } +} + +/**************************************************************************** + * Name: sdc_fault_handler + ****************************************************************************/ + +static void sdc_fault_handler(const char *file, const uint32_t line) +{ + _alert("SoftDevice Controller Fault\n"); Review comment: I think we should make this _alert wlerr we reserve _alert for very few places, and you will already get the context from the assert. ########## File path: arch/arm/src/nrf52/nrf52_sdc.c ########## @@ -0,0 +1,609 @@ +/**************************************************************************** + * arch/arm/src/chip/nrf52_sdc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/net/bluetooth.h> +#include <nuttx/wireless/bluetooth/bt_hci.h> +#include <nuttx/wireless/bluetooth/bt_driver.h> +#include <nuttx/irq.h> +#include <nuttx/arch.h> +#include <arch/armv7-m/nvicpri.h> +#include <arch/nrf52/nrf52_irq.h> +#include <nuttx/wqueue.h> + +#include "arm_internal.h" +#include "ram_vectors.h" +#include "arm_arch.h" + +#include <mpsl.h> +#include <sdc.h> +#include <sdc_hci.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if defined(CONFIG_SDC_SLAVE_COUNT) && \ + CONFIG_SDC_SLAVE_COUNT > CONFIG_BLUETOOTH_MAX_CONN +# error "Cannot support more BLE slave roles than connections" +#endif + +#define SDC_MASTER_COUNT (CONFIG_BLUETOOTH_MAX_CONN - \ + CONFIG_NRF52_SDC_SLAVE_COUNT) + +/* Todo: check central/peripheral against master/slave count */ + +#define MASTER_MEM_SIZE (SDC_MEM_PER_MASTER_LINK( \ + SDC_DEFAULT_TX_PACKET_SIZE, \ + SDC_DEFAULT_RX_PACKET_SIZE, \ + SDC_DEFAULT_TX_PACKET_COUNT, \ + SDC_DEFAULT_RX_PACKET_COUNT) \ + + SDC_MEM_MASTER_LINKS_SHARED) + +#define SLAVE_MEM_SIZE (SDC_MEM_PER_SLAVE_LINK( \ + SDC_DEFAULT_TX_PACKET_SIZE, \ + SDC_DEFAULT_RX_PACKET_SIZE, \ + SDC_DEFAULT_TX_PACKET_COUNT, \ + SDC_DEFAULT_RX_PACKET_COUNT) \ + + SDC_MEM_SLAVE_LINKS_SHARED) + +#define MEMPOOL_SIZE ((CONFIG_NRF52_SDC_SLAVE_COUNT * SLAVE_MEM_SIZE) + \ + (SDC_MASTER_COUNT * MASTER_MEM_SIZE)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nrf52_sdc_dev_s +{ + uint8_t mempool[MEMPOOL_SIZE]; + uint8_t msg_buffer[HCI_MSG_BUFFER_MAX_SIZE]; + + sem_t exclsem; + struct work_s work; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void mpsl_assert_handler(const char *const file, const uint32_t line); +static void sdc_fault_handler(const char *file, const uint32_t line); + +static int bt_open(FAR const struct bt_driver_s *btdev); +static int bt_hci_send(FAR const struct bt_driver_s *btdev, + FAR struct bt_buf_s *buf); + +static void on_hci(void); +static void on_hci_worker(void *arg); + +static void low_prio_worker(void *arg); + +static int swi_isr(int irq, FAR void *context, FAR void *arg); +static int power_clock_isr(int irq, FAR void *context, FAR void *arg); + +static void rng_handler(void); +static void rtc0_handler(void); +static void timer0_handler(void); +static void radio_handler(void); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct bt_driver_s g_bt_driver = +{ + .head_reserve = 0, + .open = bt_open, + .send = bt_hci_send +}; + +static const mpsl_clock_lfclk_cfg_t g_clock_config = +{ + .source = MPSL_CLOCK_LF_SRC_XTAL, + .rc_ctiv = 0, + .rc_temp_ctiv = 0, + .accuracy_ppm = CONFIG_NRF52_SDC_CLOCK_ACCURACY, + .skip_wait_lfclk_started = false +}; + +static struct nrf52_sdc_dev_s g_sdc_dev; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: bt_open + ****************************************************************************/ + +static int bt_open(FAR const struct bt_driver_s *btdev) +{ + return 0; +} + +/**************************************************************************** + * Name: bt_open + ****************************************************************************/ + +static int bt_hci_send(FAR const struct bt_driver_s *btdev, + FAR struct bt_buf_s *buf) +{ + int ret = OK; + irqstate_t flags; + + /* Pass HCI CMD/DATA to SDC */ + + if (buf->type == BT_CMD) + { + struct bt_hci_cmd_hdr_s *cmd = (struct bt_hci_cmd_hdr_s *)buf->data; + + wlinfo("passing CMD %d to softdevice\n", cmd->opcode); + + /* Ensure non-concurrent access to SDC operations */ + + flags = enter_critical_section(); + + nxsem_wait_uninterruptible(&g_sdc_dev.exclsem); + + if (sdc_hci_cmd_put(buf->data) < 0) + { + wlerr("sdc_hci_cmd_put() failed\n"); + ret = -EIO; + } + + nxsem_post(&g_sdc_dev.exclsem); + + leave_critical_section(flags); + + work_queue(LPWORK, &g_sdc_dev.work, on_hci_worker, NULL, 0); + } + else if (buf->type == BT_ACL_OUT) + { + wlinfo("passing ACL to softdevice\n"); + + /* Ensure non-concurrent access to SDC operations */ + + flags = enter_critical_section(); + + nxsem_wait_uninterruptible(&g_sdc_dev.exclsem); + + if (sdc_hci_data_put(buf->data) < 0) + { + wlerr("sdc_hci_data_put() failed\n"); + ret = -EIO; + } + + nxsem_post(&g_sdc_dev.exclsem); + + leave_critical_section(flags); + + work_queue(LPWORK, &g_sdc_dev.work, on_hci_worker, NULL, 0); + } + + if (ret < 0) + { + wlerr("bt_hci_send() failed: %d\n", ret); + return ret; + } + else + { + return buf->len; + } +} + +/**************************************************************************** + * Name: sdc_fault_handler + ****************************************************************************/ + +static void sdc_fault_handler(const char *file, const uint32_t line) +{ + _alert("SoftDevice Controller Fault\n"); + up_assert(file, line); +} + +/**************************************************************************** + * Name: mpsl_assert_handler + ****************************************************************************/ + +static void mpsl_assert_handler(const char *const file, const uint32_t line) +{ + _alert("MPSL assertion failed\n"); + up_assert(file, line); +} + +/**************************************************************************** + * Name: low_prio_worker + ****************************************************************************/ + +static void low_prio_worker(void *arg) +{ + /* Invoke MPSL low priority process handler. This will call on_hci() + * internally when required. + */ + + nxsem_wait_uninterruptible(&g_sdc_dev.exclsem); + mpsl_low_priority_process(); + nxsem_post(&g_sdc_dev.exclsem); +} + +/**************************************************************************** + * Name: on_hci_worker + ****************************************************************************/ + +static void on_hci_worker(void *arg) +{ + /* We use this worker to force a call to on_hci() right after sending + * an HCI command as MPSL/SDC does not always signal the low priority + * worker + */ + + nxsem_wait_uninterruptible(&g_sdc_dev.exclsem); + on_hci(); + nxsem_post(&g_sdc_dev.exclsem); +} + +/**************************************************************************** + * Name: on_hci + ****************************************************************************/ + +static void on_hci(void) +{ + struct bt_buf_s *outbuf; + size_t len; + int ret; + bool check_again = true; + + while (check_again) + { + check_again = false; + + /* Check for EVT by trying to get pending data into a generic + * buffer and then create an actual bt_buf_s, depending on msg length + */ + + ret = sdc_hci_evt_get(g_sdc_dev.msg_buffer); + + if (ret == 0) + { + struct bt_hci_evt_hdr_s *hdr = + (struct bt_hci_evt_hdr_s *)g_sdc_dev.msg_buffer; + + len = sizeof(*hdr) + hdr->len; + +#ifdef CONFIG_DEBUG_WIRELESS_INFO + if (hdr->evt == BT_HCI_EVT_CMD_COMPLETE) + { + struct hci_evt_cmd_complete_s *cmd_complete = + (struct hci_evt_cmd_complete_s *) + (g_sdc_dev.msg_buffer + sizeof(*hdr)); + uint8_t *status = (uint8_t *)cmd_complete + 1; + + wlinfo("received CMD_COMPLETE from softdevice " + "(opcode: %d, status: 0x%x)\n", + cmd_complete->opcode, *status); + } + else + { + wlinfo("received HCI EVT from softdevice " + "(evt: %d, len: %zu)\n", hdr->evt, len); + } +#endif + + outbuf = bt_buf_alloc(BT_EVT, NULL, BLUETOOTH_H4_HDRLEN); + bt_buf_extend(outbuf, len); + + memcpy(outbuf->data, g_sdc_dev.msg_buffer, len); + + bt_hci_receive(outbuf); + + check_again = true; + } + + /* Same for ACL */ + + ret = sdc_hci_data_get(g_sdc_dev.msg_buffer); + + if (ret == 0) + { + struct bt_hci_acl_hdr_s *hdr = + (struct bt_hci_acl_hdr_s *)g_sdc_dev.msg_buffer; + + wlinfo("received HCI ACL from softdevice (handle: %d)\n", + hdr->handle); + + len = sizeof(*hdr) + hdr->len; + + outbuf = bt_buf_alloc(BT_ACL_IN, NULL, BLUETOOTH_H4_HDRLEN); + bt_buf_extend(outbuf, len); + + memcpy(outbuf->data, g_sdc_dev.msg_buffer, len); + + bt_hci_receive(outbuf); + + check_again = true; + } + } +} + +/**************************************************************************** + * Name: swi_isr + ****************************************************************************/ + +static int swi_isr(int irq, FAR void *context, FAR void *arg) +{ + work_queue(LPWORK, &g_sdc_dev.work, low_prio_worker, NULL, 0); + + return 0; +} + +/**************************************************************************** + * Name: rng_handler + ****************************************************************************/ + +static void rng_handler(void) +{ + sdc_RNG_IRQHandler(); Review comment: I don't have a strong feeling here (ignoring is fine), but one thing we could do to help with the mix case error where we are calling a library function is to map them using a define in a header. Then we only have a single file that has the violations we have to deal with. ``` #define sdc_rng_irqhandler() sdc_RNG_IRQHandler() ``` ########## File path: arch/arm/src/nrf52/.gitignore ########## @@ -0,0 +1,4 @@ +CMSIS_5* Review comment: We can remove the libraries you are no longer fetching here. ########## File path: arch/arm/src/nrf52/nrf52_sdc.c ########## @@ -0,0 +1,609 @@ +/**************************************************************************** + * arch/arm/src/chip/nrf52_sdc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/net/bluetooth.h> +#include <nuttx/wireless/bluetooth/bt_hci.h> +#include <nuttx/wireless/bluetooth/bt_driver.h> +#include <nuttx/irq.h> +#include <nuttx/arch.h> +#include <arch/armv7-m/nvicpri.h> +#include <arch/nrf52/nrf52_irq.h> +#include <nuttx/wqueue.h> + +#include "arm_internal.h" +#include "ram_vectors.h" +#include "arm_arch.h" + +#include <mpsl.h> +#include <sdc.h> +#include <sdc_hci.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if defined(CONFIG_SDC_SLAVE_COUNT) && \ + CONFIG_SDC_SLAVE_COUNT > CONFIG_BLUETOOTH_MAX_CONN +# error "Cannot support more BLE slave roles than connections" +#endif + +#define SDC_MASTER_COUNT (CONFIG_BLUETOOTH_MAX_CONN - \ + CONFIG_NRF52_SDC_SLAVE_COUNT) + +/* Todo: check central/peripheral against master/slave count */ + +#define MASTER_MEM_SIZE (SDC_MEM_PER_MASTER_LINK( \ + SDC_DEFAULT_TX_PACKET_SIZE, \ + SDC_DEFAULT_RX_PACKET_SIZE, \ + SDC_DEFAULT_TX_PACKET_COUNT, \ + SDC_DEFAULT_RX_PACKET_COUNT) \ + + SDC_MEM_MASTER_LINKS_SHARED) + +#define SLAVE_MEM_SIZE (SDC_MEM_PER_SLAVE_LINK( \ + SDC_DEFAULT_TX_PACKET_SIZE, \ + SDC_DEFAULT_RX_PACKET_SIZE, \ + SDC_DEFAULT_TX_PACKET_COUNT, \ + SDC_DEFAULT_RX_PACKET_COUNT) \ + + SDC_MEM_SLAVE_LINKS_SHARED) + +#define MEMPOOL_SIZE ((CONFIG_NRF52_SDC_SLAVE_COUNT * SLAVE_MEM_SIZE) + \ + (SDC_MASTER_COUNT * MASTER_MEM_SIZE)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nrf52_sdc_dev_s +{ + uint8_t mempool[MEMPOOL_SIZE]; + uint8_t msg_buffer[HCI_MSG_BUFFER_MAX_SIZE]; + + sem_t exclsem; + struct work_s work; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void mpsl_assert_handler(const char *const file, const uint32_t line); +static void sdc_fault_handler(const char *file, const uint32_t line); + +static int bt_open(FAR const struct bt_driver_s *btdev); +static int bt_hci_send(FAR const struct bt_driver_s *btdev, + FAR struct bt_buf_s *buf); + +static void on_hci(void); +static void on_hci_worker(void *arg); + +static void low_prio_worker(void *arg); + +static int swi_isr(int irq, FAR void *context, FAR void *arg); +static int power_clock_isr(int irq, FAR void *context, FAR void *arg); + +static void rng_handler(void); +static void rtc0_handler(void); +static void timer0_handler(void); +static void radio_handler(void); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct bt_driver_s g_bt_driver = +{ + .head_reserve = 0, + .open = bt_open, + .send = bt_hci_send +}; + +static const mpsl_clock_lfclk_cfg_t g_clock_config = +{ + .source = MPSL_CLOCK_LF_SRC_XTAL, + .rc_ctiv = 0, + .rc_temp_ctiv = 0, + .accuracy_ppm = CONFIG_NRF52_SDC_CLOCK_ACCURACY, + .skip_wait_lfclk_started = false +}; + +static struct nrf52_sdc_dev_s g_sdc_dev; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: bt_open + ****************************************************************************/ + +static int bt_open(FAR const struct bt_driver_s *btdev) +{ + return 0; +} + +/**************************************************************************** + * Name: bt_open + ****************************************************************************/ + +static int bt_hci_send(FAR const struct bt_driver_s *btdev, + FAR struct bt_buf_s *buf) +{ + int ret = OK; + irqstate_t flags; + + /* Pass HCI CMD/DATA to SDC */ + + if (buf->type == BT_CMD) + { + struct bt_hci_cmd_hdr_s *cmd = (struct bt_hci_cmd_hdr_s *)buf->data; + + wlinfo("passing CMD %d to softdevice\n", cmd->opcode); + + /* Ensure non-concurrent access to SDC operations */ Review comment: I see you already have g_sdc_dev.exclsem why not use this? ########## File path: arch/arm/src/nrf52/sdc/nrf.h ########## @@ -0,0 +1,84 @@ +/**************************************************************************** + * arch/arm/src/chip/nrf52_sdc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF52_SDC_NRF_H__ +#define __ARCH_ARM_SRC_NRF52_SDC_NRF_H__ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This is the only definition we need from NRFX. We could simply do a + * typedef to an integer but we cannot ensure it will end up being the right + * size, so we replicate all values here. Review comment: This does not actually solve the issue and one reason I _hate_ when enums are used on an API boundary. The ABI is set by the compiler flags that build the library ARM for arm-none-eabi does not have hard restriction on this, but the nrf SDK uses the `-fshort-enums` flag for gcc so we can be fairly sure that is a signed short, so I would say we should just typedef this and cast. ########## File path: arch/arm/src/nrf52/nrf52_sdc.c ########## @@ -0,0 +1,609 @@ +/**************************************************************************** + * arch/arm/src/chip/nrf52_sdc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/net/bluetooth.h> +#include <nuttx/wireless/bluetooth/bt_hci.h> +#include <nuttx/wireless/bluetooth/bt_driver.h> +#include <nuttx/irq.h> +#include <nuttx/arch.h> +#include <arch/armv7-m/nvicpri.h> +#include <arch/nrf52/nrf52_irq.h> +#include <nuttx/wqueue.h> + +#include "arm_internal.h" +#include "ram_vectors.h" +#include "arm_arch.h" + +#include <mpsl.h> +#include <sdc.h> +#include <sdc_hci.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if defined(CONFIG_SDC_SLAVE_COUNT) && \ + CONFIG_SDC_SLAVE_COUNT > CONFIG_BLUETOOTH_MAX_CONN +# error "Cannot support more BLE slave roles than connections" +#endif + +#define SDC_MASTER_COUNT (CONFIG_BLUETOOTH_MAX_CONN - \ + CONFIG_NRF52_SDC_SLAVE_COUNT) + +/* Todo: check central/peripheral against master/slave count */ + +#define MASTER_MEM_SIZE (SDC_MEM_PER_MASTER_LINK( \ + SDC_DEFAULT_TX_PACKET_SIZE, \ + SDC_DEFAULT_RX_PACKET_SIZE, \ + SDC_DEFAULT_TX_PACKET_COUNT, \ + SDC_DEFAULT_RX_PACKET_COUNT) \ + + SDC_MEM_MASTER_LINKS_SHARED) + +#define SLAVE_MEM_SIZE (SDC_MEM_PER_SLAVE_LINK( \ + SDC_DEFAULT_TX_PACKET_SIZE, \ + SDC_DEFAULT_RX_PACKET_SIZE, \ + SDC_DEFAULT_TX_PACKET_COUNT, \ + SDC_DEFAULT_RX_PACKET_COUNT) \ + + SDC_MEM_SLAVE_LINKS_SHARED) + +#define MEMPOOL_SIZE ((CONFIG_NRF52_SDC_SLAVE_COUNT * SLAVE_MEM_SIZE) + \ + (SDC_MASTER_COUNT * MASTER_MEM_SIZE)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nrf52_sdc_dev_s +{ + uint8_t mempool[MEMPOOL_SIZE]; + uint8_t msg_buffer[HCI_MSG_BUFFER_MAX_SIZE]; + + sem_t exclsem; + struct work_s work; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void mpsl_assert_handler(const char *const file, const uint32_t line); +static void sdc_fault_handler(const char *file, const uint32_t line); + +static int bt_open(FAR const struct bt_driver_s *btdev); +static int bt_hci_send(FAR const struct bt_driver_s *btdev, + FAR struct bt_buf_s *buf); + +static void on_hci(void); +static void on_hci_worker(void *arg); + +static void low_prio_worker(void *arg); + +static int swi_isr(int irq, FAR void *context, FAR void *arg); +static int power_clock_isr(int irq, FAR void *context, FAR void *arg); + +static void rng_handler(void); +static void rtc0_handler(void); +static void timer0_handler(void); +static void radio_handler(void); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct bt_driver_s g_bt_driver = +{ + .head_reserve = 0, + .open = bt_open, + .send = bt_hci_send +}; + +static const mpsl_clock_lfclk_cfg_t g_clock_config = +{ + .source = MPSL_CLOCK_LF_SRC_XTAL, + .rc_ctiv = 0, + .rc_temp_ctiv = 0, + .accuracy_ppm = CONFIG_NRF52_SDC_CLOCK_ACCURACY, + .skip_wait_lfclk_started = false +}; + +static struct nrf52_sdc_dev_s g_sdc_dev; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: bt_open + ****************************************************************************/ + +static int bt_open(FAR const struct bt_driver_s *btdev) +{ + return 0; +} + +/**************************************************************************** + * Name: bt_open + ****************************************************************************/ + +static int bt_hci_send(FAR const struct bt_driver_s *btdev, + FAR struct bt_buf_s *buf) +{ + int ret = OK; + irqstate_t flags; + + /* Pass HCI CMD/DATA to SDC */ + + if (buf->type == BT_CMD) + { + struct bt_hci_cmd_hdr_s *cmd = (struct bt_hci_cmd_hdr_s *)buf->data; + + wlinfo("passing CMD %d to softdevice\n", cmd->opcode); + + /* Ensure non-concurrent access to SDC operations */ + + flags = enter_critical_section(); + + nxsem_wait_uninterruptible(&g_sdc_dev.exclsem); + + if (sdc_hci_cmd_put(buf->data) < 0) + { + wlerr("sdc_hci_cmd_put() failed\n"); + ret = -EIO; + } + + nxsem_post(&g_sdc_dev.exclsem); + + leave_critical_section(flags); + + work_queue(LPWORK, &g_sdc_dev.work, on_hci_worker, NULL, 0); + } + else if (buf->type == BT_ACL_OUT) + { + wlinfo("passing ACL to softdevice\n"); + + /* Ensure non-concurrent access to SDC operations */ + + flags = enter_critical_section(); + + nxsem_wait_uninterruptible(&g_sdc_dev.exclsem); + + if (sdc_hci_data_put(buf->data) < 0) + { + wlerr("sdc_hci_data_put() failed\n"); + ret = -EIO; + } + + nxsem_post(&g_sdc_dev.exclsem); + + leave_critical_section(flags); + + work_queue(LPWORK, &g_sdc_dev.work, on_hci_worker, NULL, 0); + } + + if (ret < 0) + { + wlerr("bt_hci_send() failed: %d\n", ret); + return ret; + } + else + { + return buf->len; + } +} + +/**************************************************************************** + * Name: sdc_fault_handler + ****************************************************************************/ + +static void sdc_fault_handler(const char *file, const uint32_t line) +{ + _alert("SoftDevice Controller Fault\n"); + up_assert(file, line); +} + +/**************************************************************************** + * Name: mpsl_assert_handler + ****************************************************************************/ + +static void mpsl_assert_handler(const char *const file, const uint32_t line) +{ + _alert("MPSL assertion failed\n"); + up_assert(file, line); +} + +/**************************************************************************** + * Name: low_prio_worker + ****************************************************************************/ + +static void low_prio_worker(void *arg) +{ + /* Invoke MPSL low priority process handler. This will call on_hci() + * internally when required. + */ + + nxsem_wait_uninterruptible(&g_sdc_dev.exclsem); + mpsl_low_priority_process(); + nxsem_post(&g_sdc_dev.exclsem); +} + +/**************************************************************************** + * Name: on_hci_worker + ****************************************************************************/ + +static void on_hci_worker(void *arg) +{ + /* We use this worker to force a call to on_hci() right after sending + * an HCI command as MPSL/SDC does not always signal the low priority + * worker + */ + + nxsem_wait_uninterruptible(&g_sdc_dev.exclsem); + on_hci(); + nxsem_post(&g_sdc_dev.exclsem); +} + +/**************************************************************************** + * Name: on_hci + ****************************************************************************/ + +static void on_hci(void) +{ + struct bt_buf_s *outbuf; + size_t len; + int ret; + bool check_again = true; + + while (check_again) + { + check_again = false; + + /* Check for EVT by trying to get pending data into a generic + * buffer and then create an actual bt_buf_s, depending on msg length + */ + + ret = sdc_hci_evt_get(g_sdc_dev.msg_buffer); + + if (ret == 0) + { + struct bt_hci_evt_hdr_s *hdr = + (struct bt_hci_evt_hdr_s *)g_sdc_dev.msg_buffer; + + len = sizeof(*hdr) + hdr->len; + +#ifdef CONFIG_DEBUG_WIRELESS_INFO + if (hdr->evt == BT_HCI_EVT_CMD_COMPLETE) + { + struct hci_evt_cmd_complete_s *cmd_complete = + (struct hci_evt_cmd_complete_s *) + (g_sdc_dev.msg_buffer + sizeof(*hdr)); + uint8_t *status = (uint8_t *)cmd_complete + 1; + + wlinfo("received CMD_COMPLETE from softdevice " + "(opcode: %d, status: 0x%x)\n", + cmd_complete->opcode, *status); + } + else + { + wlinfo("received HCI EVT from softdevice " + "(evt: %d, len: %zu)\n", hdr->evt, len); + } +#endif + + outbuf = bt_buf_alloc(BT_EVT, NULL, BLUETOOTH_H4_HDRLEN); + bt_buf_extend(outbuf, len); + + memcpy(outbuf->data, g_sdc_dev.msg_buffer, len); + + bt_hci_receive(outbuf); + + check_again = true; + } + + /* Same for ACL */ + + ret = sdc_hci_data_get(g_sdc_dev.msg_buffer); + + if (ret == 0) + { + struct bt_hci_acl_hdr_s *hdr = + (struct bt_hci_acl_hdr_s *)g_sdc_dev.msg_buffer; + + wlinfo("received HCI ACL from softdevice (handle: %d)\n", + hdr->handle); + + len = sizeof(*hdr) + hdr->len; + + outbuf = bt_buf_alloc(BT_ACL_IN, NULL, BLUETOOTH_H4_HDRLEN); + bt_buf_extend(outbuf, len); + + memcpy(outbuf->data, g_sdc_dev.msg_buffer, len); + + bt_hci_receive(outbuf); + + check_again = true; + } + } +} + +/**************************************************************************** + * Name: swi_isr + ****************************************************************************/ + +static int swi_isr(int irq, FAR void *context, FAR void *arg) +{ + work_queue(LPWORK, &g_sdc_dev.work, low_prio_worker, NULL, 0); + + return 0; +} + +/**************************************************************************** + * Name: rng_handler + ****************************************************************************/ + +static void rng_handler(void) +{ + sdc_RNG_IRQHandler(); +} + +/**************************************************************************** + * Name: power_clock_isr + ****************************************************************************/ + +static int power_clock_isr(int irq, FAR void *context, FAR void *arg) +{ + MPSL_IRQ_CLOCK_Handler(); + + return 0; +} + +/**************************************************************************** + * Name: rtc0_handler + ****************************************************************************/ + +static void rtc0_handler(void) +{ + MPSL_IRQ_RTC0_Handler(); +} + +/**************************************************************************** + * Name: timer0_handler + ****************************************************************************/ + +static void timer0_handler(void) +{ + MPSL_IRQ_TIMER0_Handler(); +} + +/**************************************************************************** + * Name: radio_handler + ****************************************************************************/ + +static void radio_handler(void) +{ + MPSL_IRQ_RADIO_Handler(); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +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. SWI5 will be + * used by MPSL to delegate low-priority work + */ + + irq_attach(NRF52_IRQ_SWI5_EGU5, swi_isr, NULL); + irq_attach(NRF52_IRQ_POWER_CLOCK, power_clock_isr, NULL); + + up_enable_irq(NRF52_IRQ_SWI5_EGU5); + up_enable_irq(NRF52_IRQ_POWER_CLOCK); + + up_prioritize_irq(NRF52_IRQ_SWI5_EGU5, NVIC_SYSH_PRIORITY_DEFAULT); + up_prioritize_irq(NRF52_IRQ_POWER_CLOCK, NVIC_SYSH_PRIORITY_DEFAULT); + + /* Use zero-latency interrupt for RNG as we're expected to not add any + * processing to the ISR + */ + + arm_ramvec_attach(NRF52_IRQ_RNG, rng_handler); + up_prioritize_irq(NRF52_IRQ_RNG, NVIC_SYSH_MAXNORMAL_PRIORITY); + up_enable_irq(NRF52_IRQ_RNG); + + /* 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); + + /* TODO: how do WFI again after high priority interrupt wakes MCU up? */ Review comment: Not sure what you are asking here. It if is a the WFI in up_idle and there is nothing READY then it will just continue the idle loop and hit WFI again. ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org