Некоторый контекст
В настоящее время я работаю с Bluetooth Low Energy над встроенной системой с использованием Zephyr.
Во время обнаружения служб/характеристик/CCCD (мое встроенное устройство является центральным устройством BLE) я хочу сохранить все характеристики, чтобы иметь возможность использовать их позже (в основном читать их).
Для этого я использую связанный список , предоставленный Zephyr, и выделяю свой узел с помощью 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;
}
Как видите, я хочу сохранить структуру bt_gatt_chrc *chrc
, передаваемую в аргументах функции. Однако эта структура содержит постоянный указатель на другую структуру с именем bt_uuid
. Эта структура bt_uuid важна, поскольку она понадобится мне для считывания правильных характеристик с периферийного устройства BLE.
Узел моего связанного списка не имеет непосредственно структуры bt_gatt_chrc
, но имеет один и тот же элемент один за другим. Это потому, что я не могу напрямую скопировать bt_gatt_chrc из аргумента в узел связанного списка, потому что константный указатель на bt_uuid
имеет тот же адрес при обнаружении служб, характеристик и CCCD (он меняется при обнаружении другого типа атрибута GATT, но для этого он останется тем же тип атрибута).
Проблема
После создания узла и копирования в него данных значение bt_uuid отличается от того, которое я получил от периферийного устройства BLE.
Вот журнал для одного стандартного 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
Я чувствую себя довольно глупо, потому что, AFAIU, моя проблема явно связана с этой линией *new_node->uuid = *chrc->uuid;
, но я не понимаю, что делаю неправильно. Я попытался преобразовать const struct bt_uuid
просто в struct bt_uuid
, результат тоже был не очень хорошим (это выглядит еще более неправильным, поскольку весь 128-битный UUID меняется между каждой стандартной характеристикой (определенной Bluetooth SIG) вместо того, чтобы оставаться довольно близким к то же самое (например, между характеристиками меняются всего 2-3 цифры без приведения структуры).
Это потому, что bt_uuid
— это «предварительный» тип, и поэтому я не могу использовать его таким образом? Может ли кто-нибудь перефразировать, что такое предварительный тип? Я явно не уверен в своем понимании этого.
Я думаю, вы имели в виду не «предварительный» тип, а «неполный». Но если бы тип был неполным, вы не смогли бы использовать к нему sizeof
или =
.
k_calloc(1, sizeof(new_node))
выглядит крайне подозрительно. Разве вместо этого не должно быть sizeof *new_node
? То же самое и с new_node->uuid = k_calloc(1, sizeof(new_node->uuid));
.
@IanAbbott Я сказал «предварительно», потому что так говорится в документации Zephyr
@KamilCuk О, я не знал об этой реализации наследования в C, это довольно интересно
Из 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];
};
struct bt_uuid *
должен указывать на достаточно памяти для хранения struct bt_uuid_16
, struct bt_uuid_32
или struct bt_uuid_128
, в зависимости от значения члена type
BT_UUID_TYPE_16
, BT_UUID_TYPE_32
или BT_UUID_TYPE_128
.
Обновлено: строка lst_chrc_node_t *new_node = k_calloc(1, sizeof(new_node));
в исходном коде OP неверна (замечена @chux в комментарии ниже и @ach в комментарии к вопросу), и исправлена в код ниже.
Непроверенный, модифицированный код:
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;
}
new_node = k_calloc(1, sizeof(new_node));
выглядит неправильно. Вы хотели new_node = k_calloc(1, sizeof(new_node[0]));
?
@chux Спасибо, я внес исправление.
Я понятия не имею, что такое предварительный тип, но он выглядит как docs.zephyrproject.org/apidoc/latest/uuid_8h_source.html хранилище для uuid зависит от типа, и пространство выделяется динамически. Это один из дешевых способов реализации наследования в C.