Zephyr precise data bus error, BLE bt_uuid_to_str and selected bt_gatt_discover() weirnesses

172 views Asked by At

I' advertising a primary BLE service. Using the nRF android app everything is full ok. Another scanning device wants to find the advert, connect, and get a list of GATT services and list their UUIDs. Scanning and connecting works fine. I know for sure that I'm connected to my device and not some other thing. Discovery is initialized as:

    static struct bt_gatt_discover_params pars;
    pars.func = gattDiscoveryCb;
    pars.type = BT_GATT_DISCOVER_PRIMARY;

    if (bt_gatt_discover(connection, &pars) != 0) {
        LOG_ERR("BLE GATT discovery error");
        return;
    }

No error here, discovery starts. I understand (I hope) the documentation here, that params->type indicates what kind of thing I'm discovering (must be a BT_GATT_DISCOVER_PRIMARY because the way discovery is initialized), then attr->user_data is a struct bt_gatt_service_val, and uuid in there is the thing I want to print for now. I have seen many examples on the internet, and there is plenty in the zephyr SDK folder too (for example ./zephyr/subsys/bluetooth/audio/vcp_vol_ctlr.c) for doing the same. The procedure is casting user_data to the proper struct, then making a string from the uuid with bt_uuid_to_str. Sound easy:

uint8_t gattDiscoveryCb(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) {
    char uuid_str[BT_UUID_STR_LEN];
    if (params->type == BT_GATT_DISCOVER_PRIMARY) {
        struct bt_gatt_service_val *service = (struct bt_gatt_service_val *)attr->user_data;
        bt_uuid_to_str(service->uuid, uuid_str, sizeof(uuid_str));

        LOG_INF("Primary service UUID: %s", uuid_str);

    } 

    return BT_GATT_ITER_CONTINUE;
}

However, on the bt_uuid_to_str line the whole thing blow up and it says:

00> [00:00:00.400,054] <err> os: ***** BUS FAULT *****
00> [00:00:00.400,054] <err> os:   Precise data bus error
00> [00:00:00.400,085] <err> os:   BFAR Address: 0xef8008f3[0m
00> [00:00:00.400,085] <err> os: r0/a1:  0xef8008f3  r1/a2:  0x20005448  r2/a3:  0x00000025
00> [00:00:00.400,115] <err> os: r3/a4:  0xef8008f3 r12/ip:  0x00000000 r14/lr:  0x00010e49
00> [00:00:00.400,115] <err> os:  xpsr:  0x21000000
00> [00:00:00.400,146] <err> os: Faulting instruction address (r15/pc): 0x00013962
00> [00:00:00.400,177] <err> os: >>> ZEPHYR FATAL ERROR 25: Unknown error on CPU 0
00> [00:00:00.400,207] <err> os: Current thread: 0x20001d60 (unknown)
00> [00:00:01.627,502] <err> fatal_error: Resetting system[0m

And here I unfortunately don't understand what's happening. Any help would be much appreciated!

Update: as probably many of you have suspected attr == NULL is sad but true. But why?

1

There are 1 answers

0
Gambanishu Habbeba On BEST ANSWER

The bus error is a consequence of trying to use the null pointer as if it pointed to data. What this documentation fails to explain clearly is that the discovery callback is called either when something is discovered... or when something is not discovered. :D (Engineering at its best...) The hint and the critical sentence is on this page that talks about the callback function:

"If discovery procedure has completed this callback will be called with attr set to NULL."

Therefore the relation between params->type and attr->user_data is not as simple as the documentation suggests, as the callback will probably be called with params->type being BT_GATT_DISCOVER_PRIMARY and attr being NULL.

Unless the discovery is stopped by returning BT_GATT_ITER_STOP the callback will be called:

  • once for each discovered item, with attr->user_data set to the corresponding thing
  • once at the end of the discovery process when there are no more items to discover with attr set to NULL

It is also worth mentioning that the bt_gatt_discover_params struct passed in is modified by the bt_gatt_discover() function call: the start_handle will be changed as items are discovered. (Do not try to use this value for anything useful! The handle of the discovered item is NOT there) This behaviour means that depending on the use case start_handle has to be set over and over again between consecutive bt_gatt_discover() calls.

Another interesting (confusing) thin is that the documentation of the discover function gives a hind of the item types possible to discover: primary service, include service, characteristic, descriptor. This is controlled by the type member of the bt_gatt_discover_params passed in. (BT_GATT_DISCOVER_PRIMARY, BT_GATT_DISCOVER_INCLUDE, BT_GATT_DISCOVER_CHARACTERISTIC, BT_GATT_DISCOVER_DESCRIPTOR respectively). The documentation of the callback function states that the value of the param->type is left unchanged. Yet the callback function's page lists two more possible values for params->type: BT_GATT_DISCOVER_STD_CHAR_DESC and BT_GATT_DISCOVER_ATTRIBUTE. (Attiubute is extra interessant, because every item possibly discovered is an attribute. And in fact when discovering attributes everything is found...) This must mean that the bt_gatt_discover() functions documentation is missing two possible types.

The last thing I want to point out is the type of the attr->user_data in the callback. The callback's documentation states in the table that when discovering descriptors or attributes the attr->user_data is NULL. This is not true. Both descriptor and attribute discovery types return struct bt_gatt_scc in attr->user_data in case of CCC (and possibly CEP, SCC and CPF, maybe even the others). On the other hand discovering with BT_GATT_DISCOVER_STD_CHAR_DESC type where these structs should be properly returned discovers nothing at all.