Usage Fault when implementing an advertiser sent callback.

NCS 2.7.0

NRF52840 DK

I am trying to trigger an advertising sent callback to indicate advertising timeout or a maximum number of events. I have tried increasing the workqueue and main thread stack sizes but still get a usage fault error. 

Here is my code for a simple Bluetooth paging service. 

#include <zephyr/kernel.h>
#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/device.h>
#include <zephyr/drivers/pwm.h>
#include <soc.h>

// For power manager
#include <zephyr/init.h>
#include <zephyr/pm/pm.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/policy.h>
#include <zephyr/sys/poweroff.h>
#include <zephyr/sys/util.h>

// Devicetree + GPIO includes
#include <nrfx_gpiote.h>
#include <helpers/nrfx_gppi.h>
#include <nrfx_ppi.h>
#include <zephyr/irq.h>

// Bluetooth include files
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/services/bas.h>

const struct device *const cons = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));

#define GREEN_LED_NODE      DT_ALIAS(led1)
#define BLUE_LED_NODE       DT_ALIAS(led2)
#define RED_LED_NODE        DT_ALIAS(led0)
#define BUTTON_NODE         DT_ALIAS(sw0)

#if CONFIG_BOARD_PRO_FLAG
#define HAPTIC_PWM_NODE     DT_ALIAS(pwm_buzz)
#define HAPTIC_PIN_NODE     DT_ALIAS(buzz_en)
#define PWM_RED_LED_NODE    DT_ALIAS(pwm_led0)
#define PWM_GREEN_LED_NODE  DT_ALIAS(pwm_led1)
#define PWM_BLUE_LED_NODE   DT_ALIAS(pwm_led2)
#define USB_DETECT_NODE     DT_ALIAS(usb_dt)
#define USB_STAT_NODE       DT_ALIAS(chrg)
#define FACTORY_ENABLE_NODE DT_ALIAS(pwron)
#else
#define HAPTIC_PWM_NODE DT_ALIAS(pwm_led0)
#define HAPTIC_PIN_NODE DT_ALIAS(led3)
#endif

static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(BUTTON_NODE, gpios);
//static const struct gpio_dt_spec r_led =  GPIO_DT_SPEC_GET(RED_LED_NODE, gpios);
static const struct gpio_dt_spec g_led =  GPIO_DT_SPEC_GET(GREEN_LED_NODE, gpios);
static const struct gpio_dt_spec b_led =  GPIO_DT_SPEC_GET(BLUE_LED_NODE, gpios);
#if CONFIG_BOARD_PRO_FLAG
static const struct pwm_dt_spec pwm_ledr =    PWM_DT_SPEC_GET(PWM_RED_LED_NODE);
static const struct pwm_dt_spec pwm_ledg =    PWM_DT_SPEC_GET(PWM_GREEN_LED_NODE);
//static const struct pwm_dt_spec pwm_ledb =    PWM_DT_SPEC_GET(PWM_BLUE_LED_NODE);
static const struct gpio_dt_spec usb_detect = GPIO_DT_SPEC_GET(USB_DETECT_NODE, gpios);
static const struct gpio_dt_spec stat_pin =   GPIO_DT_SPEC_GET(USB_STAT_NODE, gpios);
static const struct gpio_dt_spec bat_en =     GPIO_DT_SPEC_GET(FACTORY_ENABLE_NODE, gpios);
#else
static const struct pwm_dt_spec pwm_ledr = PWM_DT_SPEC_GET(HAPTIC_PWM_NODE);
static const struct pwm_dt_spec pwm_ledg = PWM_DT_SPEC_GET(HAPTIC_PWM_NODE);
static const struct pwm_dt_spec pwm_ledb = PWM_DT_SPEC_GET(HAPTIC_PWM_NODE);
#endif
static const struct pwm_dt_spec pwm_buzz = PWM_DT_SPEC_GET(HAPTIC_PWM_NODE);
static const struct gpio_dt_spec buzz_en = GPIO_DT_SPEC_GET(HAPTIC_PIN_NODE, gpios);

//Total fade up time is DELAY * PWM_STEPS
#define HAPTIC_PWM_PERIOD_NS    500000
#define HAPTIC_FADE_DELAY_MS 1
#define HAPTIC_PWM_STEPS        1000

#define POWERON_PWM_PERIOD_NS 500000
#define POWERON_FADE_UP_DELAY_MS 1
#define POWERON_PWM_STEPS 3000

#define DEVICE_NAME "rareBit PRO Flag"
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)

#define MAX_TRANSMIT_SIZE 240//TODO figure this out

// Define the custom services and characteristics

#define PAGING_SERVICE_UUID 0x6d, 0x1b, 0xee, 0x1e, 0xee, 0x7d, 0xd0, 0xba, 0x7b, \
                            0x4b, 0xd5, 0x28, 0x01, 0x00, 0x21, 0x23

// Characteristic: Paging Characteristic UUID 23210002-28D5-4B7B-BA0F-7DEE1EEE1B6D
#define PAGE_ALERT_CHARACTERISTIC_UUID 0x6d, 0x1b, 0xee, 0x1e, 0xee, 0x7d, 0xd0, 0xba, 0x7b, \
                                 0x4b, 0xd5, 0x28, 0x02, 0x00, 0x21, 0x23

#define BT_UUID_PAGING_SERVICE              BT_UUID_DECLARE_128(PAGING_SERVICE_UUID)
#define BT_UUID_PAGE_ALERT_CHARACTERISTIC   BT_UUID_DECLARE_128(PAGE_ALERT_CHARACTERISTIC_UUID)

//Advertising Timeout period ranges from EVENTS * MIN_INTERVAL to EVENTS * MAX_INTERVAL
#define ADVERTISNG_TIMEOUT_EVENTS 200

#define WORQ_THREAD_STACK_SIZE  8192
#define WORKQ_PRIORITY   5

static bool app_button_state = false;


// Define stack area used by workqueue thread
static K_THREAD_STACK_DEFINE(my_stack_area, WORQ_THREAD_STACK_SIZE);

// Define queue structure
static struct k_work_q offload_work_q = {0};


struct work_info {
    struct k_work work;
    char name[25];
} radio_work;

struct usb_work_info
{
    struct k_work work;
    char name[50];
} usb_work;

uint8_t data_tx[MAX_TRANSMIT_SIZE];
bool debounce_check = false;


static struct bt_le_ext_adv *adv;
struct bt_conn *conn_handle = NULL;
const struct bt_gatt_attr *page_alert_attr = NULL;

bool page_flag = false;

int my_service_init(void)
{
    int err = 0;

    memset(&data_tx, 0, MAX_TRANSMIT_SIZE);

    return err;
}

// Bluetooth advertising data
static const struct bt_data ad[] = {
    BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
    BT_DATA_BYTES(BT_DATA_UUID128_ALL,
		      0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86,
		      0xd3, 0x4c, 0xb7, 0x1d, 0x1d, 0xdc, 0x53, 0x8d),
    BT_DATA_BYTES(BT_DATA_UUID128_ALL, PAGING_SERVICE_UUID)
};

static void advertising_max_events(struct bt_le_ext_adv *adv, struct bt_le_ext_adv_sent_info *info)
{

    uint8_t adv_events = 200;//info->num_sent;

    printk("Adertising finished with %d events.\n", adv_events);

    //Giving time for the print buffer. 
    //We need this under every print that occurs before PM_DEVICE_ACTION_SUSPEND.
    k_msleep(1000);
    
    gpio_pin_set_dt(&b_led, 0);

    //Effectively the timeout condition, but it's not a precise interval
    if (adv_events >= ADVERTISNG_TIMEOUT_EVENTS)
    {
        printk("Advertising timed out. Shutting down... \n");
    }
    else
    {
        //Else is effectively the connected condition (which we already have a callback for... idk)
        printk("Probably connected.\n");
    }
}

static void create_advertising_coded(void)
{
    int err;

    struct bt_le_adv_param params = BT_LE_ADV_PARAM_INIT(BT_LE_ADV_OPT_CONNECTABLE |
                                                        BT_LE_ADV_OPT_CODED |
                                                        BT_LE_ADV_OPT_EXT_ADV,
                                                        BT_GAP_ADV_FAST_INT_MIN_2,
                                                        BT_GAP_ADV_FAST_INT_MAX_2,
                                                        NULL);
    struct bt_le_ext_adv_cb advertising_cb =
        {
            .sent = advertising_max_events
        };

    err = bt_le_ext_adv_create(&params, &advertising_cb, &adv);
    if(err)
    {
        printk("Error %d could not create ext_adv.\n", err);
    }
    else printk("CODED PHY Advertising Configured.\n");

    err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
    if(err)
    {
        printk("Error %d could not set_data.\n", err);
    }
    else printk("CODED PHY adv data set.\n");
}

void start_advertising_coded(void)
{
    int err;

    //timeout value doesn't seem to work here, the advertising sent function gets triggered even when the timeout doesn't occur
    struct bt_le_ext_adv_start_param start_params;

    //Minimum advertising intverval is 100ms, thus a timeout period of AT LEAST num_events*100ms
    start_params.num_events = ADVERTISNG_TIMEOUT_EVENTS; 

    err = bt_le_ext_adv_start(adv, &start_params);
    if(err)
    {
        printk("Error: Advertising NOT started. return %d\n", err);

    }
    else 
	{
		printk("Bluetooth advertising started!\n");
        gpio_pin_set_dt(&b_led, 1);
		
	}

}

// Instantiate the service and its characteristics
BT_GATT_SERVICE_DEFINE(
    paging_service,
    
    // Simple Service
    BT_GATT_PRIMARY_SERVICE(BT_UUID_PAGING_SERVICE),

    // Page Alert Characteristic
    BT_GATT_CHARACTERISTIC(BT_UUID_PAGE_ALERT_CHARACTERISTIC,
                    BT_GATT_CHRC_NOTIFY, 
                    BT_GATT_PERM_NONE,
                    NULL,
                    NULL,
                    NULL),
    BT_GATT_CCC(NULL, BT_GATT_PERM_WRITE | BT_GATT_PERM_READ)
   
);


// Connected callback function
static void connected_cb(struct bt_conn *conn, uint8_t err)
{

    struct bt_conn_info info;
    char addr[BT_ADDR_LE_STR_LEN];

    conn_handle = conn;

    page_alert_attr = &paging_service.attrs[2];

	if (err) 
	{
		printk("Connection failed (err %u)\n", err);
		return;
	}
	else if(bt_conn_get_info(conn, &info))
	{
		printk("Could not parse connection info\n");
	}
	else
	{
		bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
		
		printk("\n \n Connection established!\n\
		Connected to: %s					 \n\
		Role: %u							 \n\
		Connection interval: %u				 \n\
		Slave latency: %u					 \n\
		Connection supervisory timeout: %u	 \n"
		, addr, info.role, info.le.interval, info.le.latency, info.le.timeout);
	}
}

// Disconnected callback function
static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
{
    printk("Disconnected with reason %d\n", reason);
    k_sleep(K_MSEC(100));

    conn_handle = NULL;
    
    gpio_pin_set_dt(&b_led, 0);

    start_advertising_coded();

    gpio_pin_set_dt(&g_led, 0);

    
}

#if LE_PARAMETER_REQUESTS_ENABLED
static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
{
	//If acceptable params, return true, otherwise return false.
	return true; 
}

static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout)
{
	struct bt_conn_info info; 
	char addr[BT_ADDR_LE_STR_LEN];
	
	if(bt_conn_get_info(conn, &info))
	{
		printk("Could not parse connection info\n");
	}
	else
	{
		bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
		
		printk("Connection parameters updated!	\n\
		Connected to: %s						\n\
		New Connection Interval: %u				\n\
		New Slave Latency: %u					\n\
		New Connection Supervisory Timeout: %u	\n"
		, addr, info.le.interval, info.le.latency, info.le.timeout);
	}
}
#endif

// Connection callback structure
static struct bt_conn_cb conn_callbacks = {
    .connected = connected_cb,
    .disconnected = disconnected_cb
#if LE_PARAMETER_REQUESTS_ENABLED
    ,
    .le_param_req			= le_param_req,
	.le_param_updated		= le_param_updated
#endif
};


void bt_pageAlert(struct bt_conn *conn, const uint8_t *data, uint16_t len)
{
    
    // Send the notification
    if(bt_gatt_notify(conn, page_alert_attr, &data, len))
    {
        printk("Error, unable to send notification\n");
        
    }
    else {
        printk("Notified Peer of %d\n", (int) &data);
    }

}

void offload_function(struct k_work *work_tem)
{
    app_button_state = !app_button_state;

    uint8_t page_d[] = {0x01};
	uint8_t * page_p = page_d;

	if ((conn_handle != NULL) & app_button_state)
    {
        bt_pageAlert(conn_handle, page_p, 1);
    }
    
    printk("Work thread executed. \n");
    k_yield();
}

static void button_changed(const struct device *dev, struct gpio_callback *cb,
                           uint32_t pins)
{

    k_work_submit_to_queue(&offload_work_q, &radio_work.work);
    k_yield();
    
}

static void init_interrupts(void)
{
    int err;

    err = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_BOTH);
    if (err < 0)
    {
        printk("Button pin interrupt configure failed with err %d\n", err);
    }
    else
    {
        printk("Button pin interrupt configured.\n");
    }

    static struct gpio_callback button_cb_data;

    gpio_init_callback(&button_cb_data, button_changed, BIT(button.pin));

    err = gpio_add_callback(button.port, &button_cb_data);
    if (err < 0)
    {
        printk("Button pin interrupt callback add failed with err %d\n", err);
    }
    else
    {
        printk("Button pin interrupt callback added.\n");
    }
}

static void init_pins(void)
{
    int err;

    err = gpio_pin_configure_dt(&button, GPIO_INPUT);
    if (err < 0)
    {
        printk("GPIO Input Button failed (err %d)\n", err);
    }
    else
        printk("Button input configured\n");


}

int main(void)
{
    
    init_pins();
    init_interrupts();

    k_work_queue_start(&offload_work_q, my_stack_area, K_THREAD_STACK_SIZEOF(my_stack_area), WORKQ_PRIORITY, NULL);
    strcpy(radio_work.name, "Bluetooth Notify thread");
    k_work_init(&radio_work.work, offload_function);

    int err;

    err = bt_enable(NULL);
    if (err)
    {
        printk("Bluetooth init failed (err %d)\n", err);
    }

    err = my_service_init();
    if (err)
    {
        printk("Could not initialize simple service \n");
    }

    // Register for connection callbacks
    bt_conn_cb_register(&conn_callbacks);

    // Create the Extended Advertising
    create_advertising_coded();

    // Start advertising
    start_advertising_coded();

    return 0;
}
5852.prj.conf

Error Message:

[00:00:20.874,969] <err> os: ***** USAGE FAULT *****
[00:00:20.875,000] <err> os:   Illegal use of the EPSR
[00:00:20.875,000] <err> os: r0/a1:  0x200025c8  r1/a2:  0x2000685c  r2/a3:  0x000000c8
[00:00:20.875,030] <err> os: r3/a4:  0x000185e0 r12/ip:  0x00001407 r14/lr:  0x0001bd0f
[00:00:20.875,030] <err> os:  xpsr:  0x20000000
[00:00:20.875,030] <err> os: Faulting instruction address (r15/pc): 0x000185e0
[00:00:20.875,091] <err> os: >>> ZEPHYR FATAL ERROR 35: Unknown error on CPU 0
[00:00:20.875,122] <err> os: Current thread: 0x200024f0 (BT RX WQ)
[00:00:20.953,125] <err> os: Halting system

Parents Reply
  • Hi,

    I was not able to reproduce at first, and seemingly unrelated changed seemed to affect it, pointing to usage of invalid memory. And that seems to be the case, as your adverticing advertising_cb struct is neither global nor static. And this will cause problems if the memory is re-used. The API docuemntation for bt_le_ext_adv_create() specifies that this callback truct parameters "Must point to valid memory during the lifetime of the advertising set". So making it static (or global) should solve the issue.

Children
Related