Some context
I'm currently working with Bluetooth Low Energy on an embedded system using Zephyr.
During the services/characteristics/CCCDs discovering (my embedded device is the BLE central), I want to store every characteristics so I can use them later (mainly read them).
For that I'm using the linked list provide by Zephyr and I allocate my node using k_calloc()
.
typedef struct lst_chrc_node_s {
sys_snode_t node;
struct bt_uuid *uuid;
uint16_t value_handle;
uint8_t properties;
uint8_t type;
} lst_chrc_node_t;
lst_chrc_node_t *list_create_node(struct bt_gatt_chrc *chrc, uint8_t type)
{
lst_chrc_node_t *new_node = k_calloc(1, sizeof(new_node));
if (!new_node)
return NULL;
new_node->uuid = k_calloc(1, sizeof(new_node->uuid));
if (!new_node->uuid)
{
k_free(new_node);
return NULL;
}
print_uuid128("UUID that will be store", chrc->uuid);
*new_node->uuid = *chrc->uuid;
new_node->value_handle = chrc->value_handle;
new_node->properties = chrc->properties;
new_node->type = type;
print_uuid128("UUID that was stored ", new_node->uuid);
return new_node;
}
As you can see, I want to store the struct bt_gatt_chrc *chrc
that is passed in the function's arguments. However, this structure contains a constant pointer to an other struct named bt_uuid
. This bt_uuid struct is important because I will need it to read the correct characteristic from the BLE peripheral.
My linked list's node doesn't have directly the bt_gatt_chrc
struct but has the same member one by one. This because I can't directly copy the bt_gatt_chrc from argument to linked list's node because the const pointer to bt_uuid
has the same address when discovering services, characteristics and CCCDs (It change when discovering a different GATT Attribute's type but will still the same for this type of attribute).
The problem
After creating the node and copying data in it, the bt_uuid value isn't the same as the one I received from the BLE peripheral.
Here is the log for one standard UUID:
[00:00:04.293,914] <inf> main: UUID received UUID 128: 070002a4-5720-0045-2020-0013072a0000
[00:00:04.293,945] <inf> main: UUID that will be store UUID 128: 070002a4-5720-0045-2020-0013072a0000
[00:00:04.293,975] <inf> main: UUID that was stored UUID 128: 00000d00-0d61-8c00-0200-020003000000
I feel pretty stupid because AFAIU, my issue seems clearly to be in this line *new_node->uuid = *chrc->uuid;
but I don't see what I'm doing wrong. I tried to cast the const struct bt_uuid
to just struct bt_uuid
, the result wasn't good either (it looks like even more wrong as the entire 128-bit UUID changes between each standard characteristics (defined by the Bluetooth SIG) instead of staying pretty close to the same (like just 2-3 digits change between characteristics without casting the struct).
Is it because the bt_uuid
is a "tentative" type and so I can't use it this way ? Can someone rephrase what is a tentative type ? I'm clearly not sure about my understanding of this.
From uuid.h:
enum {
BT_UUID_TYPE_16,
BT_UUID_TYPE_32,
BT_UUID_TYPE_128,
};
#define BT_UUID_SIZE_16 2
#define BT_UUID_SIZE_32 4
#define BT_UUID_SIZE_128 16
struct bt_uuid {
uint8_t type;
};
struct bt_uuid_16 {
struct bt_uuid uuid;
uint16_t val;
};
struct bt_uuid_32 {
struct bt_uuid uuid;
uint32_t val;
};
struct bt_uuid_128 {
struct bt_uuid uuid;
uint8_t val[BT_UUID_SIZE_128];
};
The struct bt_uuid *
should point to enough memory to store a struct bt_uuid_16
, a struct bt_uuid_32
, or a struct bt_uuid_128
, depending on the value of the type
member BT_UUID_TYPE_16
, BT_UUID_TYPE_32
, or BT_UUID_TYPE_128
.
EDIT: The line lst_chrc_node_t *new_node = k_calloc(1, sizeof(new_node));
in OP's original code is wrong (spotted by @chux in the comment below, and @ach in the comment on the question), and has been corrected in the code below.
Untested, modified code:
lst_chrc_node_t *list_create_node(struct bt_gatt_chrc *chrc, uint8_t type)
{
lst_chrc_node_t *new_node;
size_t bt_uuid_size;
switch (chrc->uuid->type)
{
case BT_UUID_TYPE_16:
bt_uuid_size = sizeof(struct bt_uuid_16);
break;
case BT_UUID_TYPE_32:
bt_uuid_size = sizeof(struct bt_uuid_32);
break;
case BT_UUID_TYPE_128:
bt_uuid_size = sizeof(struct bt_uuid_128);
break;
default:
return NULL;
}
new_node = k_calloc(1, sizeof(*new_node));
if (!new_node)
return NULL;
new_node->uuid = k_calloc(1, bt_uuid_size);
if (!new_node->uuid)
{
k_free(new_node);
return NULL;
}
print_uuid128("UUID that will be store", chrc->uuid);
memcpy(new_node->uuid, chrc->uuid, bt_uuid_size);
new_node->value_handle = chrc->value_handle;
new_node->properties = chrc->properties;
new_node->type = type;
print_uuid128("UUID that was stored ", new_node->uuid);
return new_node;
}